get max from table where sum required - sql-server

Suppose I have a table with following data:
gameId difficultyLevel numberOfQuestions
--------------------------------------------
1 1 2
1 2 2
1 3 1
In this example the game is configured for 5 questions, but I'm looking for a SQL statement that will work for n number of questions.
What I need is a SQL statement that given a question, displayOrder will return the current difficulty level of question. For example - given a displayOrder of 3, with the table data above, will return 2.
Can anyone advise how the query should look like?

I'd recommend a game table with a 1:m relationship with a question table.
You shouldn't repeat columns in a table - it violates first normal form.
Something like this:
create table if not exists game
(
game_id bigint not null auto_increment,
name varchar(64),
description varchar(64),
primary key (game_id)
);
create table if not exists question
(
question_id bigint not null auto_increment,
text varchar(64),
difficulty int default 1,
game_id bigint,
primary key (question_id) ,
foreign key game_id references game(game_id)
);
select
game.game_id, name, description, question_id, text, difficulty
game left join question
on game.game_id = question.game_id
order by question_id;

things might be easier for you if you change your design as duffymo suggests, but if you must do it that way, here's a query that should do the trick.
SELECT MIN(difficultyLevel) as difficltyLevel
FROM
(
SELECT difficltyLevel, (SELECT sum(numberOfQuestions) FROM yourtable sub WHERE sub.difficultyLevel <= yt.difficultyLevel ) AS questionTotal
FROM yourTable yt
) AS innerSQL
WHERE innerSQL.questionTotal >= #displayOrder

Related

Select a large volume of data with like SQL server

I have a table with ID column
ID column is like this : IDxxxxyyy
x will be 0 to 9
I have to select row with ID like ID0xxx% to ID3xxx%, there will be around 4000 ID with % wildcard from ID0000% to ID3999%.
It is like combining LIKE with IN
Select * from TABLE where ID in (ID0000%,ID0001%,...,ID3999%)
I cannot figure out how to select with this condition.
If you have any idea, please help.
Thank you so much!
You can use pattern matching with LIKE. e.g.
WHERE ID LIKE 'ID[0-3][0-9][0-9][0-9]%'
Will match an string that:
Starts with ID (ID)
Then has a third character that is a number between 0 and 3 [0-3]
Then has 3 further numbers ([0-9][0-9][0-9])
This is not likely to perform well at all. If it is not too late to alter your table design, I would separate out the components of your Identifier and store them separately, then use a computed column to store your full id e.g.
CREATE TABLE T
(
NumericID INT NOT NULL,
YYY CHAR(3) NOT NULL, -- Or whatever type makes up yyy in your ID
FullID AS CONCAT('ID', FORMAT(NumericID, '0000'), YYY),
CONSTRAINT PK_T__NumericID_YYY PRIMARY KEY (NumericID, YYY)
);
Then your query is a simple as:
SELECT FullID
FROM T
WHERE NumericID >= 0
AND NumericID < 4000;
This is significantly easier to read and write, and will be significantly faster too.
This should do that, it will get all the IDs that start with IDx, with x that goes form 0 to 4
Select * from TABLE where ID LIKE 'ID[0-4]%'
You can try :
Select * from TABLE where id like 'ID[0-3][0-9]%[a-zA-Z]';

SQL to get teams with no power forward

I store data about basketball teams in my Teams table that looks like this:
CREATE TABLE Teams (
Id varchar(5) NOT NULL PRIMARY KEY,
TeamName varchar(50) NOT NULL
);
And I keep team members in TeamMembers table that looks like this:
CREATE TABLE TeamMembers (
Id int NOT NULL PRIMARY KEY,
TeamId VARCHAR(5) FOREIGN KEY REFERENCES Teams(Id),
LastName varchar(50) NOT NULL,
FirstName varchar(50) NOT NULL,
PositionId int NOT NULL
);
Positions are in another table with INT ID's. For example, Guard: 1, Center: 2 and Power Forward: 3 in this exercise.
I want to get a list of basketball teams with NO power forward.
Something like:
select *
from Teams
where Id not in
(
select TeamId
from TeamMembers
where PositionID = 4
)
When checking if a row doesn't exist, use a NOT EXISTS!.
SELECT
T.*
FROM
Teams AS T
WHERE
NOT EXISTS (
SELECT
'team has no power forward member'
FROM
TeamMembers AS M
WHERE
M.TeamID = T.ID AND
M.PositionID = 3) -- 3: Power Forward

Create table 6 x 6 with automatic spill from upline

I am creating a code to company MMN. the idea is a system which has a table 6 x 6 with automatic spill.
For example.
I register 6 new persons.
John
Peter
Mary
Lary
Anderson
Paul
When I register my 7th the system automatic follow the order below me and put into John network. When I register the 8th the system automatic follow the order below me and put into Peter network.
Table 6 x 6
Firt level: 6
Second level: 36
I am trying to creating a test with stored procedure in sqlserver.
I am stuck in the part how I can do automatically put the new person registered to below me when I reach the limit of the table.
Creating a Matrix would be denormalizing your data. It is usually best practice NOT to do this, as it makes data manipulation a lot more difficult, among other reasons. How would you prevent the rows from being more than 6? You'd have to add a weird constraint like so:
create table #matrix ( ID int identity(1,1),
Name1 varchar(64),
Name2 varchar(64),
Name3 varchar(64),
Name4 varchar(64),
Name5 varchar(64),
Name6 varchar(64),
CONSTRAINT ID_PK PRIMARY KEY (ID),
CONSTRAINT Configuration_SixRows CHECK (ID <= 6))
I'm betting you aren't doing this, and thus, you can't "ensure" no more than 6 rows is inserted into your table. If you are doing this, then you'd have to insert data one row at a time which goes against everything SQL Server is about. This would be to check if the first column is full yet, then move to the second, then the third, etc... it just doesn't make sense.
Instead, I would create a ParentID column to relate your names to their respective network as you stated. This can be done with a computed column like so:
declare #table table (ID int identity(1,1),
Names varchar(64),
ParentID as case
when ID <= 6 then null
else replace(ID % 6,0,6)
end)
insert into #table
values
('John')
,('Peter')
,('Mary')
,('Lary')
,('Anderson')
,('Paul')
,('Seven')
,('Eight')
,('Nine')
,('Ten')
,('Eleven')
,('Twelve')
,('Thirteen')
,('Fourteen')
select * from #table
Then, if you wanted to display it in a matrix you would use PIVOT(), specifically Dynamic Pivot. There are a lot of examples on Stack Overflow on how to do this. This also accounts for if you want the matrix to be larger than 6 X N... perhaps the network grows so each member has 50 individuals... thus 6 (rows) X 51 (columns)
IF it's only going to be 6 columns, or not many more, then you can also use a simple join logic...
select
t.ID
,t.Names
,t2.Names
,t3.Names
from #table t
left join
#table t2 on t2.ParentID = t.ID and t2.ID = t.ID + 6
left join
#table t3 on t3.ParentID = t.ID and t3.ID = t.ID + 12
--continue on
where
t.ParentID is null
You can see this in action with This OnLine DEMO
Here is some information on normalization

How can I assign an order value to rows in a table based on a value of frequence contained in a column

I have this table:
CREATE TABLE [dbo].[Phrase] (
[PhraseId] UNIQUEIDENTIFIER DEFAULT (newid()) NOT NULL,
[English] NVARCHAR (250) NOT NULL,
[Kana] NVARCHAR (MAX) NULL,
[Kanji] NVARCHAR (MAX) NULL,
[FrequencyA] INT NULL,
[OrderA] INT NULL,
PRIMARY KEY CLUSTERED ([PhraseId] ASC),
CONSTRAINT [FK_PhrasePhraseChapter] FOREIGN KEY ([ChapterId]) REFERENCES [dbo].[PhraseChapter] ([PhraseChapterShortId])
);
The Freq column contains a number from 0 to 20,000.
Is there a way that I can populate the [OrderA] column so as to assign a position to each row. Here's an example of what I mean:
PhraseId FrequencyA OrderA
1 100 3
2 50 4
3 50 5
4 201 1
5 200 2
6 10 7
7 50 6
I realize this is rather difficult to explain so please ask any questions if the question is not clear and I will try to clarify. Note that for the case of the row with the Order of 50 I am not concerned about the order for the PhraseId of 2,3 and 7 as long as one of them appears as the 4th, another the 5th and another the 6th. I have about 15,000 rows so slight differences like this will not make any difference.
You can use RANKING functions to get the desired result.
Adding the code here.
SELECT PhraseId, FrequencyA, ROW_NUMBER() OVER(PARTITION BY (SELECT NULL) ORDER BY FrequencyA DESC) AS OrderA
FROM Phrase
ORDER BY PhraseId
Note: I want to highlight that the colunm PhraseId is a UNIQUEIDENTIFIER, which will not show the numbers as integers as shown in the example, instead it will show GUID.
Adding the UPDATE Query.
;WITH PhraseUpdate
AS
(
SELECT PhraseId, FrequencyA, ROW_NUMBER() OVER(PARTITION BY (SELECT NULL) ORDER BY FrequencyA DESC) AS OrderA
FROM Phrase
)
UPDATE p
SET OrderA = pu.OrderA
FROM PhraseUpdate pu
JOIN Phrase p
ON p.PhraseId = pu.PhraseId

TSQL to insert a set of rows and dependent rows

I have 2 tables:
Order (with a identity order id field)
OrderItems (with a foreign key to order id)
In a stored proc, I have a list of orders that I need to duplicate. Is there a good way to do this in a stored proc without a cursor?
Edit:
This is on SQL Server 2008.
A sample spec for the table might be:
CREATE TABLE Order (
OrderID INT IDENTITY(1,1),
CustomerName VARCHAR(100),
CONSTRAINT PK_Order PRIMARY KEY (OrderID)
)
CREATE TABLE OrderItem (
OrderID INT,
LineNumber INT,
Price money,
Notes VARCHAR(100),
CONSTRAINT PK_OrderItem PRIMARY KEY (OrderID, LineNumber),
CONSTRAINT FK_OrderItem_Order FOREIGN KEY (OrderID) REFERENCES Order(OrderID)
)
The stored proc is passed a customerName of 'fred', so its trying to clone all orders where CustomerName = 'fred'.
To give a more concrete example:
Fred happens to have 2 orders:
Order 1 has line numbers 1,2,3
Order 2 has line numbers 1,2,4,6.
If the next identity in the table was 123, then I would want to create:
Order 123 with lines 1,2,3
Order 124 with lines 1,2,4,6
On SQL Server 2008 you can use MERGE and the OUTPUT clause to get the mappings between the original and cloned id values from the insert into Orders then join onto that to clone the OrderItems.
DECLARE #IdMappings TABLE(
New_OrderId INT,
Old_OrderId INT)
;WITH SourceOrders AS
(
SELECT *
FROM Orders
WHERE CustomerName = 'fred'
)
MERGE Orders AS T
USING SourceOrders AS S
ON 0 = 1
WHEN NOT MATCHED THEN
INSERT (CustomerName )
VALUES (CustomerName )
OUTPUT inserted.OrderId,
S.OrderId INTO #IdMappings;
INSERT INTO OrderItems
SELECT New_OrderId,
LineNumber,
Price,
Notes
FROM OrderItems OI
JOIN #IdMappings IDM
ON IDM.Old_OrderId = OI.OrderID

Resources