How to apply answer key on students' responses - sql

I conducted a MCQs type test for students in Eng, math, and science
Students solved their test on a sheet readable by the OMR/OCR.
The machine produced responses of the students in follwoig way:-
Student ID Subject Q1 Q2 Q3 Q4 ......
201 English 3 1 4 1 ......
201 Math 3 2 1 1 ....
201 Science 2 1 2 3 ....
202 English 3 1 4 1 ......
202 Math 3 2 1 1 ....
202 Science 2 1 2 3 ....
-
-
-
How can i apply answer key on this data e.g for English my answer key is:
2 2 4 1 .....
would you pleas suggest a query for this situation

SELECT
StudentId
,CASE WHEN Q2 = ?ANSWER? then 1 ELSE 0 END
,CASE WHEN Q3 = ?ANSWER? then 1 ELSE 0 END
,CASE WHEN Q4 = ?ANSWER? then 1 ELSE 0 END
,CASE WHEN Q5 = ?ANSWER? then 1 ELSE 0 END
FROM
Table
Where subject = 'English'
One of possible solutions replace ?ANSWER? with something from your answer key
Other would be to generate tables like:
CREATE TABLE EnglishKey(
QuestionId int,
Answer int)
Then insert your values in this way:
INSERT INTO EnglishKey VALUES (1,3),(2,4),(....)
Replacing 1 with question number and answer with proper answer.
And then creating a SELECT should be easy

SELECT
a.StudentID
CASE WHEN a.Q1 <> a.A1 THEN 0 ELSE 1 end AS Result1,
CASE WHEN a.Q2 <> a.A2 THEN 0 ELSE 1 end AS Result2,
CASE WHEN a.Q3 <> a.A3 THEN 0 ELSE 1 end AS Result3,
CASE WHEN a.Q4 <> a.A4 THEN 0 ELSE 1 end AS Result4,
CASE WHEN a.Q5 <> a.A5 THEN 0 ELSE 1 end AS Result5,
CASE WHEN a.Q6 <> a.A6 THEN 0 ELSE 1 end AS Result6,
CASE WHEN a.Q7 <> a.A7 THEN 0 ELSE 1 end AS Result7,
CASE WHEN a.Q8 <> a.A8 THEN 0 ELSE 1 end AS Result8
FROM
StudentTable a
INNER JOIN AnswerTable b ON a.[SUBJECT] = b.[SUBJECT]
This answer presumes that you have the answers in a seperate table and can join on subject.
If you find it a pain to write out all the case statements (could have 100s of questions) you can quickly build these queries in Microsoft Excel or open office by breaking up the statement across multiple columns, have it fill in the 1,2,3,4s etc and then copy paste once happy.

Related

SQL: SUM OR COUNT with CASE WHEN condition in multiple criteria

Course name
Section number
Course type
MATH 101
1
In person
MATH 101
2
In person
MATH 101
3
Online
MATH 101
4
In person
SOC 101
1
In person
SOC 101
2
In person
SOC 101
3
In person
ENGL 201
1
In person
ENGL 201
2
Online
ENGL 201
3
Online
ENGL 201
4
In person
PHY 101
1
Online
PHY 101
2
Online
From this table, I'd like to count Courses with only an 'In person' course, an 'Online' course, and both course types.
The query I tried is below.
SELECT
SUM(CASE WHEN coursetype = 'Inperson' AND coursetype = 'Online' THEN 1 ELSE 0 END) AS bothtype,
SUM(CASE WHEN coursetype = 'Online' THEN 1 ELSE 0 END) AS Onlineonly,
SUM(CASE WHEN coursetype = 'Inperson' THEN 1 ELSE 0 END) AS Onlineonly
From Course
The result what I expected is
bothtpye
Onlineonly
Inpersononly
2
1
1
but I got
bothtpye
Onlineonly
Inpersononly
0
7
6
Please advise me to get through this.
Thank you.
My solution uses double conditional aggregation.
SELECT SUM (CASE WHEN In_Person > 0 AND Online > 0 THEN 1 ELSE 0 END) as bothtype,
SUM (CASE WHEN In_Person > 0 AND Online = 0 THEN 1 ELSE 0 END) as inpersononly,
SUM (CASE WHEN In_Person = 0 AND Online > 0 THEN 1 ELSE 0 END) as onlineonly
FROM (
SELECT Course_name,
SUM(CASE WHEN Course_type='In Person' THEN 1 ELSE 0 END) as In_Person,
SUM(CASE WHEN Course_type='Online' THEN 1 ELSE 0 END) as Online
FROM Course
GROUP BY Course_name
) tot
DEMO Fiddle
SUGGESTION ( using PL/SQL ! ) :
CREATE PROCEDURE countCourses(OUT bothtype INT,OUT Inpersononly INT,OUT Onlineonly INT)
begin
SELECT COUNT(*) INTO bothtype FROM Course;
select COUNT(*) INTO Inpersononly FROM Course
WHERE courseType = "In person";
select COUNT(*) INTO Onlineonly FROM Course
WHERE courseType = "Online";
end;
call countCourses(#bothtype,#Inpersononly,#Onlineonly);
SELECT #bothtype,#Inpersononly,#Onlineonly;
EXPLICATION :
Creating procedure to store the count of each type of course in OUT variable
Call the procedure with convenient parameters
Select out given parameters

Creating extra columns based on condition (Case When Sum)

I have survey table where someone is asked roughly 5 questions. 4 of those questions are the same questions, but the options to their answers are different since they were to understand their purchase.
Here are the questions:
ID Question qid Answer
101005 what brands did you purchase the past 5 months 1 Coca-Cola or Pepsi or Dr.Pepper
101005 what brands did you purchase the past 5 months 1 Dr.Pepper
101005 what brands did you purchase the past 5 months 1 store brand
101005 what brands did you purchase the past 5 months 1 Coca-Cola
101005 how many people live in your house 4 4
101005 what is your prefer retailers 8 walmart
The goal is to create four extra columns based on their answer and they will be assigned a 1 or 0. Since this person's answer is coca cola, I want to assign them in the column of current_buyer and give them 1 and 0 will be new_buyer column. I also want to make sure that even though he answer Dr.Pepper in the second row, it still recognizes him as a current_buyer. In the same breath I want to assign this person a 1 in the 3rd column as a drinker and 0 4th column in prospect.
Here is how the table should look like
ID Question qid answer Current_buyer New_buyer drinker prospect
101005 what brands did you purchase the past 5 months 1 Coca-Cola or Pepsi or Dr.Pepper 1 0 1 0
101005 what brands did you purchase the past 5 months 1 Dr.Pepper 1 0 1 0
101005 how many people live in your house 4 4 1 0 1 0
The goal is to see if ID bought coca-cola the past 5 months, they are a current buyer (1) and drinker (1) and will have (0) for new_buyer and prospect in their entire profile.
Here is the code that I try:
select ID,qid,question,answer,s_date,
Case when Sum(Case when [answer] like'%coca-cola%' then 1 else 0 end)>=1 then 1
else 0 end current_buyer
,Case when Sum(Case when [answer] like'%coca-cola%' then 1 else 0 end)=0 then 1
else 0 end New_buyer
,Case when Sum(Case when [answer] like'%coca-cola,Dr.pepper,pepsi%' then 1 else 0 end)>=1 then 1
else 0 end drinker
,Case when Sum(Case when [answer] like'%coca-cola,Dr.pepper,pepsi%' then 1 else 0 end)=0 then 1
else 0 end Prospect
Unfortunately, using this code I'm getting 0 in the drinker column even though people selected coca-cola. Any help would be appreciated.
I'm not 100% sure that I understand the logic behind assigning the various values, but in general it seems that you are assigning the value based on an aggregation of multiple rows. Therefore you need to do the aggregation first (in this case in a CTE) and then join back to that for your query.
Also worth noting is that even though your are doing your matching with a LIKE operator - everything between the [%] characters will need to match exactly somewhere in your [answer]. I don't think that this is what you want - so I've split them out. However I'm not 100% sure if I've got the logical operators completely correct.
Do you need something like:
;with analysisCTE as (select ID,qid,
Case when Sum(Case when [answer] like'%coca-cola%' then 1 else 0 end)>=1 then 1
else 0 end current_buyer
,Case when Sum(Case when [answer] like'%coca-cola%' then 1 else 0 end)=0 then 1
else 0 end New_buyer
,Case when Sum(Case when [answer] like'%coca-cola%' OR [answer] like '%Dr.pepper%' OR [answer] like '%pepsi%' then 1 else 0 end)>=1 then 1
else 0 end drinker
,Case when Sum(Case when [answer] like'%coca-cola%' OR [answer] like '%Dr.pepper%' OR [answer] like '%pepsi%' then 1 else 0 end)=0 then 1
else 0 end Prospect
from drinks
group by id, qid)
select d.ID,d.qid,d.question,d.answer, a.current_buyer, a.New_buyer, a.drinker, a.Prospect
from drinks d
inner join analysisCTE a on d.id = a.id and d.qid = a.qid

group by in case of nested cases with conditions on different tables

My question is a bit similar to this question but with a caveat. In my case the conditions are dependent on different tables, not one table. The part which is giving me trouble is the GROUP BY part. Here is the query:
SELECT
CASE
WHEN T1.ImportantColumn = 'Y'
THEN 'Good'
ELSE
CASE
WHEN T2.ImportantColumn = 1
THEN 'Very Good'
ELSE
CASE
WHEN T3.ImportantColumn IS NULL
THEN 'Bad'
ELSE T3.ImportantColumn
END
END
END AS WorkStatus,
SUM(case when T2.sex = 'M' THEN 1 ELSE 0 END) male ,
SUM(case when T2.sex = 'F' THEN 1 ELSE 0 END) female ,
COUNT(WorkStatus) AS [CountWorkStatus]
FROM
Condition1Table T1
RIGHT JOIN Condition2Table T2 ON T1.city = T2.Code_id AND T1.field_name = 'cities'
INNER JOIN Condition3Table T3 ON T2.student_id = T3.student_id
GROUP BY T3.ImportantColumn, T2.ImportantColumn, T1.ImportantColumn -- <-- wrote this but I know it's wrong
It is sort of IF ELSE scenario. If Condition1Table.ImportantColumn is 'Y' then 'Good', else if Condition2Table.ImportantColumn is 1 then 'Very Good', else if Condition3Table.ImportantColumn is NULL then 'bad', Else the value in Condition3Table.ImportantColumn. The hard part is the grouping of data in a desired format which is below:
WorkStatus | male | female | CountWorkStatus
---------- ----- ------ ---------------
Good | 3 | 7 | 10
Very Good | 11 | 2 | 13
Bad | 5 | 0 | 5
Val1 | 1 | 9 | 10
Val2 | 41 | 23 | 64
You seem to be asking "how do I group by a huge CASE statement without repeating the whole CASE statement"?
If so, just use a sub-query.
Then the result of the CASE statement has a column name that you can refer to.
There is near zero performance penalty here, sub-queries are expanded out macro-like. SQL is a declarative language, it's just a syntax for expressing a problem to be solved. When that's compiled down there's a program to run. So, while thinking about the SQL, you just need the syntax to express your problem.
SELECT
WorkStatus,
SUM(case when sex = 'M' THEN 1 ELSE 0 END) male ,
SUM(case when sex = 'F' THEN 1 ELSE 0 END) female ,
COUNT(WorkStatus) AS [CountWorkStatus]
FROM
(
SELECT
CASE
WHEN T1.ImportantColumn = 'Y'
THEN 'Good'
ELSE
CASE
WHEN T2.ImportantColumn = 1
THEN 'Very Good'
ELSE
CASE
WHEN T3.ImportantColumn IS NULL
THEN 'Bad'
ELSE T3.ImportantColumn
END
END
END AS WorkStatus,
T2.sex
FROM
Condition1Table T1
RIGHT JOIN Condition2Table T2 ON T1.city = T2.Code_id AND T1.field_name = 'cities'
INNER JOIN Condition3Table T3 ON T2.student_id = T3.student_id
)
AS StatusBySex
GROUP BY
WorkStatus

SQL: Combine Duplicate Rows And Case Statement Values in Final Data Output

I'm working on a SQL query that counts duplicate records that are based on a text field I am working with: where datasource = 'Web' or 'Internal'. I am currently using a case statement to count the number of times a record shows that value. My question is how do I combine the two results into one record that shows a count of both.
I'm attaching a query of what I currently have working, what my output is and what I would like the end result to look like.
SELECT id
,lastname
,firstname
,datasource
,CASE
WHEN (
(Datasource = 'Web')
)THEN Count(Datasource)
ELSE 0
END WebData
,CASE
WHEN (
(Datasource = 'Internal')
) THEN Count(Datasource)
ELSE 0
END InternalData
,count(id) as countid
FROM Table
GROUP BY
id
,lastname
,firstname
,datasource
This currently returns:
12345 Jack Boss Internal 0 1 1
12241 Eric Graves Internal 0 1 1
13300 Su Lynn Web 1 0 1
13300 Su Lynn Internal 0 1 1
13914 Mark Ross Internal 0 2 2
14008 Mitch Smith Internal 0 1 1
I would like it to return:
12345 Jack Boss 0 1 1
12241 Eric Graves 0 1 1
13300 Su Lynn 1 1 2
13914 Mark Ross 0 2 2
14008 Mitch Smith 0 1 1
Thanks
You need to add the outer select for current query with the Group on the the columsn you want to do the aggrigate.Just add the Outer select to your query,
SELECT
data.id,
data.lastname,
data.firstname,
SUM(data.WebData) AS WebData,
SUM(data.InternalData) AS InternalData,
SUM(data.countid) AS Countid
FROM
(
SELECT id,lastname,
firstname,datasource,
CASE WHEN Datasource = 'Web' THEN Count(Datasource) ELSE 0 END WebData,
CASE WHEN Datasource = 'Internal' THEN Count(Datasource)ELSE 0 END InternalData,
count(id) AS Countid
FROM Table
GROUP BY
id,lastname,firstname,datasource
) AS data
GROUP BY
data.id,data.firstname,data.lastname

Retrieve rows which satisfy at least four of given seven conditions

I am having a table called as 'Alphabets' with columns named from 'A to G'.
Table Name: Alphabets
Column Names: A | B | C | D | E | F | G
Now, I need a SQL query to retrieve all rows from the table which satisfy at least four criteria from the following list:
A = 1
B = 2
C = 3
D = 4
E = 5
F = 6
G = 7
I am using Oracle 10g database.
SELECT * FROM Alphabets where
CASE WHEN A=1 THEN 1 ELSE 0 END +
CASE WHEN B=2 THEN 1 ELSE 0 END +
CASE WHEN C=3 THEN 1 ELSE 0 END +
CASE WHEN D=4 THEN 1 ELSE 0 END +
CASE WHEN E=5 THEN 1 ELSE 0 END +
CASE WHEN F=6 THEN 1 ELSE 0 END +
CASE WHEN G=7 THEN 1 ELSE 0 END >= 4
But something about this table and query seem suspect - It feels like it should really be a table of two columns, Letter and Value say.
One ugly solution I could think of (I'm sure there must be better solutions, but this one should work, though I'm not conviced it is optimal in any sense...)
SELECT *
FROM Alphabets
WHERE
CASE WHEN A=1 THEN 1 ELSE 0 END +
CASE WHEN B=2 THEN 1 ELSE 0 END +
CASE WHEN C=3 THEN 1 ELSE 0 END +
CASE WHEN D=4 THEN 1 ELSE 0 END +
CASE WHEN E=5 THEN 1 ELSE 0 END +
CASE WHEN F=6 THEN 1 ELSE 0 END +
CASE WHEN G=7 THEN 1 ELSE 0 END BETWEEN 4 AND 7
This has problems:
doesn't scale well - new constraint - new line in query...
performance wise it is not optimal
also just plain horribly ugly