Conditional calculation in SELECT SQL Server - sql

I have Question_Data table as below:
------------------------------
QuestionID | Attempts | Wrong
------------------------------
1 | 20 | 7
2 | 1 | 4
3 | 14 | 2
4 | 30 | 5
When a request is received, I process it and it results in a table called, "Responses Table":
(Sample Responses Table)
---------------------
QuestionID | Response
---------------------
1 | T
2 | F
3 | F
4 | F
5 | T
Now what i want is that i want to update "Question_Data" table on the basis of "Responses" table as:
New "Question_Data" table:
------------------------------
QuestionID | Attempts | Wrong
------------------------------
1 | 21 | 7
2 | 2 | 5
3 | 15 | 3
4 | 31 | 6
5 | 1 | 0
I checked QuestionID in "Question_Data" table and if it exists in "Question_Data" table I incremented it "Attempts" and if response is "F" increment "Wrong".
But if QuestionID is not present in "Question_Data" table. Insert a new row with same QuestionID and increment its "Attempts" to 1 and if response is "T" set its wrong to 0.
I am new to SQL. Any help will be appreciated.

UPDATE Q
SET Q.Attempts = Q.Attempts + 1 ,
Q.Wrong = Q.Wrong + ( CASE WHEN R.Response = 'F' THEN 1
ELSE 0
END )
FROM Question_Data Q
INNER JOIN Responses_Table R ON Q.QuestionID = R.QuestionID
After update if data is not present in Question_Data run this query
INSERT INTO Question_Data
(QuestionID ,
Attempts ,
Wrong)
SELECT
R.QuestionID ,
1,
(CASE WHEN R.Response = 'F' THEN 1
ELSE 0
END) Wrong
FROM
Responses_Table R
LEFT JOIN
Question_Data Q ON Q.QuestionID = R.QuestionID
WHERE
Q.QuestionID IS NULL

Assuming that the QuestionID is unique in both the Questions_Data and Responses table, you could use the update-join syntax:
UPDATE qd
SET Attempts = Attempts + 1,
Wrong = Wrong + CASE Response WHEN 'F' THEN 1 ELSE 0 END
FROM Questions_Data qd
JOIN Reponse r ON qd.QuestionID = r.QuestionID

Related

how to rank row for same repeating value in sql

Given this data how can give rank for each repeating data. 1 to 5 i want to rank as 1 and next 1 to 5 i want to rank as 2
Data
1
2
3
4
5
1
2
3
4
5
Expecting output
Data | Column
1 1
2 1
3 1
4 1
5 1
1 2
2 2
3 2
4 2
5 2
I was trying to implement using row number but Below is the exact requirement that i have to implement :
Refcol value column
1 refers to time
2 refers to name
3 refers to location
4 refers to Available (1 or 0 or null)
ID | Refcol | Metric
1 1 02/02/2022
1 2 Adam
1 3 Japan
1 4 1
1 1 03/02/2022
1 2 Smith
1 3 England
1 4 0
Now i want to transform above data as shown below
Expected Ouput
ID | time | name | location | Available
1 02/02/2022 Adam Japan 1
1 03/02/2022 Smith England 0
Best you can do with the limited sample data is to create a row number for each time a number appears. Then order by the row number and then the number. If this doesn't work, then show us more real data.
When using ROW_NUMBER, there's no guarantee that the first 1 through 5 group will be ordered correctly. You have to have some other identifier to guarantee the ordering (i.e. time stamp, set number, parent group, etc.).
SQL Fiddle
MS SQL Server 2017 Schema Setup:
CREATE TABLE Numbers (
num int not null
);
INSERT INTO Numbers
VALUES (1),(2),(3),(4),(5),(1),(2),(3),(4),(5)
Query 1:
WITH prelim AS (
SELECT n.num
, ROW_NUMBER() OVER(PARTITION BY n.num ORDER BY n.num ASC) as row_num
FROM Numbers as n
)
SELECT
p.num
, p.row_num
FROM prelim as p
ORDER BY p.row_num, p.num
Results:
| num | row_num |
|-----|---------|
| 1 | 1 |
| 2 | 1 |
| 3 | 1 |
| 4 | 1 |
| 5 | 1 |
| 1 | 2 |
| 2 | 2 |
| 3 | 2 |
| 4 | 2 |
| 5 | 2 |
UPDATE: If you are dead-set on not changing the data structure, then the best you can do is loop through the data and assigning a unique SetID number to each group of 4 rows. There is no guarantee that this will work when you have more than 4 rows in the table since you have no column to guarantee a consistent sort order.
CREATE TABLE attributes (
ID int not null
, RefCol int not null
, Metric nvarchar(50) not null
, SetID int null
);
INSERT INTO attributes (ID, RefCol, Metric)
VALUES
(1,1,'02/02/2022')
,(1,2,'Adam')
,(1,3,'Japan')
,(1,4,'1')
,(1,1,'03/02/2022')
,(1,2,'Smith')
,(1,3,'England')
,(1,4,'0')
;
DECLARE #setID int = 0;
WHILE (EXISTS (SELECT ID FROM attributes WHERE SetID is NULL))
BEGIN
UPDATE TOP (4) attributes
SET SetID = #setID
FROM attributes
WHERE SetID IS NULL
;
SET #setID = #setID + 1;
END
SELECT * FROM attributes;
SELECT DISTINCT
a.SetID
, a.ID
, aTime.Metric as [time]
, aName.Metric as [name]
, aLoc.Metric as [location]
, aAvail.Metric as [Available]
FROM attributes as a
LEFT OUTER JOIN attributes as aTime
ON aTime.SetID = a.SetID
AND aTime.RefCol = 1
LEFT OUTER JOIN attributes as aName
ON aName.SetID = a.SetID
AND aName.RefCol = 2
LEFT OUTER JOIN attributes as aLoc
ON aLoc.SetID = a.SetID
AND aLoc.RefCol = 3
LEFT OUTER JOIN attributes as aAvail
ON aAvail.SetID = a.SetID
AND aAvail.RefCol = 4
;
ID
RefCol
Metric
SetID
1
1
02/02/2022
0
1
2
Adam
0
1
3
Japan
0
1
4
1
0
1
1
03/02/2022
1
1
2
Smith
1
1
3
England
1
1
4
0
1
SetID
ID
time
name
location
Available
0
1
02/02/2022
Adam
Japan
1
1
1
03/02/2022
Smith
England
0
fiddle
You're probably better off asking the question you actually have.
The result you want can be achieved from the data you gave, but it's going to be non-deterministic.
DECLARE #ints TABLE (INT INT)
INSERT INTO #ints (INT) VALUES
(1), (2), (3), (4), (5),
(1), (2), (3), (4), (5)
SELECT INT, ROW_NUMBER() OVER (PARTITION BY INT ORDER BY INT) AS rn
FROM #ints
ORDER BY rn, INT
INT rn
------
1 1
2 1
3 1
4 1
5 1
1 2
2 2
3 2
4 2
5 2

how to update multiple columns while comparing them between rows in a table

i have a table say
table 1
id | rel_id
123 | 456
789 | 321
so id column from table 1 = id from table 2 and will have catgy 180
rel_id column from table 1 = id from table 2 and will have catgy 181
table 2
id | catgy | spl_1 | spl_2 | spl_3 | spl 4
123 | 180 | 6 | 0 | 0 | 7
456 | 181 | 7 | 0 | 0 | 0
789 | 180 | 8 | 9 | 9 | 0
321 | 181 | 9 | 0 | 0 | 0
so i want to comapre spl_2, spl_3, spl_4 for id 123 with spl_1 for id 456 and if same then update id 123 spl's with null(in this case update spl_4 with null)
Thanks
You can just use a standard CASE expression for it:
Update YourTable
Set SPCLTY_2 = Case When SPCLTY_2 = SPCLTY_1 Then Null Else SPCLTY_2 End,
SPCLTY_3 = Case When SPCLTY_3 = SPCLTY_1 Then Null Else SPCLTY_3 End,
SPCLTY_4 = Case When SPCLTY_4 = SPCLTY_1 Then Null Else SPCLTY_4 End,
SPCLTY_5 = Case When SPCLTY_5 = SPCLTY_1 Then Null Else SPCLTY_5 End
When you need to update values in one table based on values from another table (or the same table, or the result of a join, etc. - from another relation* would be the most general formulation), it is often easiest to do it with the MERGE statement. This is a perfect example.
*Relation is a fancy term for a table or anything that resembles
one, such as the result of a join, an aggregate operation, or really
any kind of SELECT statement.
merge into table_2 t2
using
( select t1.id, s.spl_1
from table_1 t1 join table_2 s on t1.rel_id = s.id
) x
on ( x.id = t2.id )
when matched then update
set t2.spl_2 = case when t2.spl_2 = x.spl_1 then null else t2.spl_2 end,
t2.spl_3 = case when t2.spl_3 = x.spl_1 then null else t2.spl_3 end,
t2.spl_4 = case when t2.spl_4 = x.spl_1 then null else t2.spl_4 end
where x.spl_1 in (t2.spl_2, t2.spl_3, t2.spl_4)
-- WHERE clause so that you only update rows that need to be updated!
;
2 rows merged.
select * from table_2;
ID CATGY SPL_1 SPL_2 SPL_3 SPL_4
---------- ---------- ---------- ---------- ---------- ----------
123 180 6 0 0
456 181 7 0 0 0
789 180 8 0
321 181 9 0 0 0

Need T-SQL query to get multiple choice answer if matches

Example:
Table Question_Answers:
+------+--------+
| q_id | ans_id |
+------+--------+
| 1 | 2 |
| 1 | 4 |
| 2 | 1 |
| 3 | 1 |
| 3 | 2 |
| 3 | 3 |
+------+--------+
User_Submited_Answers:
| q_id | sub_ans_id |
+------+------------+
| 1 | 2 |
| 1 | 4 |
| 2 | 1 |
| 3 | 1 |
| 3 | 2 |
| 3 | 4 |
+------+------------+
I need a T-SQL query if this rows matches count 1 else 0
SELECT
t1.q_id,
CASE WHEN COUNT(t2.sub_ans_id) = COUNT(*)
THEN 1
ELSE 0 END AS is_correct
FROM Question_Answers t1
LEFT JOIN User_Submited_Answers t2
ON t1.q_id = t2.q_id AND
t1.ans_id = t2.sub_ans_id
GROUP BY t1.q_id
Try the following code:
select qa.q_id,case when qa.ans_id=sqa.ans_id then 1 else 0 end as result from questionans qa
left join subquestionans sqa
on qa.q_id=sqa.q_id and qa.ans_id=sqa.ans_id
This should give you expected result for every question.
select q_id, min(Is_Correct)Is_Correct from (
select Q.q_id,case when count(A.sub_ans_id)=count(*) then 1 else 0 end as Is_Correct
from #Q Q left join #A A on Q.q_id=A.q_id and Q.ans_id=A.sub_ans_id
group by Q.q_id
UNION ALL
select A.q_id,case when count(Q.ans_id)=count(*) then 1 else 0 end as Is_Correct
from #Q Q right join #A A on Q.q_id=A.q_id and Q.ans_id=A.sub_ans_id
group by A.q_id ) I group by q_id
MySQL solution (sql fiddle):
SELECT tmp.q_id, MIN(c) as correct
FROM (
SELECT qa.q_id, IF(qa.q_id = usa.q_id, 1, 0) as c
FROM question_answers qa
LEFT JOIN user_submited_answers usa
ON qa.q_id = usa.q_id AND qa.ans_id = usa.sub_ans_id
UNION
SELECT usa.q_id, IF(qa.q_id = usa.q_id, 1, 0) as c
FROM question_answers qa
RIGHT JOIN user_submited_answers usa
ON qa.q_id = usa.q_id AND qa.ans_id = usa.sub_ans_id
) tmp
GROUP BY tmp.q_id;
Now, step by step explanation:
In order to get the right output we will need to:
extract from question_answers table the answers which were not filled in by the user (in your example: q_id = 3 with ans_id = 3)
extract from user_submited_answers table the wrong answers which were filled in by the user (in your example: q_id = 3 with sub_ans_id = 4)
To do that we can use a full outer join (for mysql left join + right join):
SELECT *
FROM question_answers qa
LEFT JOIN user_submited_answers usa
ON qa.q_id = usa.q_id AND qa.ans_id = usa.sub_ans_id
UNION
SELECT *
FROM question_answers qa
RIGHT JOIN user_submited_answers usa
ON qa.q_id = usa.q_id AND qa.ans_id = usa.sub_ans_id;
From the previous query results, the rows which we are looking for (wrong answers) contains NULL values (based on the case, in question_answers table or user_submited_answers table).
The next step is to mark those rows with 0 (wrong answer) using an IF or CASE statement: IF(qa.q_id = usa.q_id, 1, 0).
To get the final output we need to group by q_id and look for 0 values in the grouped rows. If there is at least one 0, the answer for that question is wrong and it should be marked as that.
Check sql fiddle: SQL Fiddle

Check if record exists in subquery

I have the following 3 tables
Source
Id | Name | SiteId
---+--------------+---------
1 | Source 1 | 1
2 | Source 2 | 1
3 | Source 3 | 2
4 | Source 4 | 2
SourceAccount
SourceId | AccountId
---------+----------
1 | 1
2 | 1
3 | 1
4 | 1
SourceUser
SourceId | UserId
---------+----------
1 | 1
3 | 1
I'm trying to build up a query with thew following parameters
SiteId (1)
AccountId (1)
UserId (1)
With those parameters the query should return something like this
SourceId | Name | Access
---------+--------------+---------
1 | Source 1 | 1
2 | Source 2 | 0
Access column being a boolean (bit)
So in essence the query should return all the sources (Id and Name) that are part of a given AccountId for a given SiteId along with a boolean telling me is a user has access to it or not.
Any idea on how to proceed?
Thanks
EDIT:
For the record here is the query I came up with but does not work:
SELECT s.[Id], s.[Name]
,(IF EXISTS (SELECT 1 FROM [dbo].[SourceUser] su WHERE su.SourceId = s.[Id] AND su.UserId = 1)
SELECT CAST(1 AS BIT)
ELSE
SELECT CAST(0 AS BIT))
FROM [dbo].[Source] s
INNER JOIN [dbo].[SourceAccount] sa ON sa.SourceId = s.Id
WHERE sa.AccountId = 1
AND s.SiteId = 1
Might also be worth mentionning that I'm running this on SQL Azure and SQL Server 2012
EDIT 2: Relations
SiteAccount - Site - SiteUser - User
| \ |
Account - SourceAccount - Source - SourceUser
Try this:
SELECT s.[Id],
s.[Name],
CASE WHEN EXISTS (SELECT 1 FROM [dbo].[SourceUser] su WHERE su.SourceId = s.[Id] AND su.UserId = 1)
THEN CAST(1 AS BIT)
ELSE CAST(0 AS BIT)
END AS Access
FROM [dbo].[Source] s
INNER JOIN [dbo].[SourceAccount] sa ON sa.SourceId = s.Id
WHERE sa.AccountId = 1 AND s.SiteId = 1

How to select extra columns while using group by clause?

I have a table which contains data in this format.
productid filterName boolfilter numericfilter
1 X 1 NULL
1 Y NULL 99inch
1 Z 0 NULL
2 Y NULL 55kg
2 Y NULL 45kg
3 K NULL 20
3 M NULL 35
3 N NULL 25
4 X 1 NULL
4 K 1 NULL
I need data in this format.
Need products where only numeric filters are setup but no boolean filters
productid filterName numericfilter
2 Y 55kg
2 Y 45kg
3 K 20
3 M 35
3 N 25
I have written this query,
SELCT productid
FROM tbl_filters
GROUP BY productid
HAVING SUM(CAST(boolfilter AS INT)) IS NULL
I am getting prouctid 2 and 3, but i need the extra columns also as i have mentioned.
When i am using multiple columns in groupby clause i am not getting the required output.
SELECT t.productid, t.filterName, t.numericfilter
FROM Table_Name t
WHERE t.numericfilter IS NOT NULL
AND NOT EXISTS (SELECT 1
FROM TABLE_NAME
WHERE t.productid = productid
AND boolfilter IS NOT NULL)
Working SQL FIDDLE
| PRODUCTID | FILTERNAME | NUMERICFILTER |
|-----------|------------|---------------|
| 2 | Y | 55kg |
| 2 | Y | 45kg |
| 3 | K | 20 |
| 3 | M | 35 |
| 3 | N | 25 |
Use window functions instead:
SELECT productid, filterName, numericfilter
FROM (SELECT f.*,
MAX(boolfilter) OVER (PARTITION BY productid) as maxbf
FROM tbl_filters f
) f
WHERE maxbf is null;
Fiddle DEMO.
This calculates the maximum of boolfilter for each productid. If it is always NULL, then the result is NULL. Note that you don't need a cast() for this.