SUM CASE WHEN (SQL) - sql

I want to apply a simple SUM CASE WHEN to a table I have created from CSV (in DB Browser for SQLite). At the moment I have the below code:
Select user_id,
CASE Overall_Result
WHEN 'Fail' then 0
WHEN 'Pass' then 1 else 0
END as "Case When"
from NewTable
This code gives me:
Image
However, now I want to amend the above code so that it SUMS the values in the created CASE WHEN column for every particular user_id. As you can see in e.g. row 1 and 2 of the image, we have the same user_id and the corresponding values in the second column. I want to combine these user_id's so there is just one instance of each in column 1 and then have the SUM of all the corresponding values for this user_id in the second column. GROUP BY doesn't seem to do this..

Do you just want aggregation?
select user_id,
SUM(CASE Overall_Result WHEN 'Fail' then 0 WHEN 'Pass' then 1 else 0
END) as sum_value
from NewTable
group by user_id;
Or, if you just want the number of passes, you can use the shorthand:
SUM(Overall_Result = 'Pass')

just add the sum() and the related group by eg:
Select user_id,
sum( CASE Overall_Result
WHEN 'Fail' then 0
WHEN 'Pass' then 1 else 0
END ) as "Case When"
from NewTable
group by user_id

Related

SQL Group by - Aggregate a column based on custom logic

I have come across a case where I need to aggregate the table based on custom logic.
Student_id
Subject
Result
123
Maths
FAILED
123
English
PASS
456
Maths
PASS
456
English
PASS
Now I want the result in the following way:
Student_id
overall_result
123
FAILED ( If a student failed in any subject, overall he is failed)
456
PASS
Any idea how can I do this using SQL?
EDITED: Here can be other values also like GRACE , SUPPLYMENT. A student is passed only and only when all values are PASS, otherwise FAILED
If your field "Result" allows only "PASSED" and "FAILED" values, you can use the aggregate function MIN to retrieve the smallest value between the two. If there's at least one "FAILED", it will get returned.
SELECT Student_id, MIN(Result)
FROM tab
GROUP BY Student_id
Check the demo here.
If you can have multiple values too, you can enforce any value different than "PASSED" to "FAILED", then get the MIN as per previous query.
SELECT Student_id,
MIN(CASE WHEN Result = 'PASS' THEN 'PASS'
ELSE 'FAILED'
END) AS Result
FROM tab
GROUP BY Student_id
Check the demo here.
We can use COUNT or SUM with CASE.
If the sum of FAILED values is > 0, this means the overall result will be FAILED, otherwise it will be PASS.
SELECT Student_id,
CASE
WHEN COUNT(CASE WHEN Result = 'FAILED' THEN 1 END) > 0
THEN 'FAILED'
ELSE 'PASS' END AS overall_result
FROM tab
GROUP BY Student_id;
If we use MySQL, the above query can be simplified to:
SELECT Student_id,
CASE
WHEN SUM(Result = 'FAILED') > 0
THEN 'FAILED'
ELSE 'PASS' END AS overall_result
FROM tab
GROUP BY Student_id;
Tested here: db<>fiddle

check and compare the count from two tables without relation

I have below tables
Table1: "Demo"
Columns: SSN, sales, Create_DT,Update_Dt
Table2: "Agent"
Columns: SSN,sales, Agent_Name, Create_Dt, Update_DT
Scenario 1 and desired result set:
I want output as 0 if the count of SSN in Demo table is matched with the count of SSN in Agent table
if the count is not matched then I want result as 1
Scenario 2 and desired result set:
I want output as 0 if the sum of sales in Demo table is matched with the sum of sales in Agent table
if the sum is not matched then I want result as 1
Please help on this query part
Thanks
You can write two queries separately to take counts within the result query
SELECT (SELECT count(Demo.SSN) as SSN1 from Demo)!=(SELECT count(Agent.SSN) as SSN2 from Agent) AS Result;
Basically what the inner queries does is it checked whether the counts are equal or not and outputs 1 if it is true and 0 if it is false. Since you have asked to output 1 if it is false I used '!=' sign.
You can try the same procedure in scenario 2 also
For scenario 1
select (Case when (select count(ssn) from Demo)=(select count(ssn) from Agent) then 0 else 1 end) as desired_result
If you want to count unique ssn then:
select (Case when (select count(distinct ssn) from Demo)=(select count(distinct ssn) from Agent) then 0 else 1 end) as desired_result
For scenario 2:
select (Case when (select sum(sales) from Demo)=(select sum(sales) from Agent) then 0 else 1 end) as desired_result
I would suggest one query with both sets of information:
select (d.num_ssn <> a.num_ssn) as have_different_ssn_count,
(d.sales <> a.sales) as have_different_sales
from (select count(distinct ssn) as num_ssn,
coalesce(sum(sales), 0) as sales
from demo
) d cross join
(select count(distinct ssn) as num_ssn,
coalesce(sum(sales), 0) as sales
from agent
) a;
Note: This returns boolean values -- true/false rather than 1/0. If you really want 0/1, then use case:
select (case when d.num_ssn <> a.num_ssn then 1 else 0 end) as have_different_ssn_count,
(case when d.sales <> a.sales then 1 else 0 end) as have_different_sales
It would not surprise me if you were not only interested in the total counts but also that the agent/sales combinations are the same in both tables. If that is the case, please ask a new question with a clear explanation. Sample data and desired results help.

Proportion request sql

There is a table of accidents and output the share of accidents number 2 to all accidents I wrote this code, but I can not make it work:
select ((select count("ID") from "DTP" where "REASON"=2)/count("REASON"))
from "DTP"
group by "ID"
Something like this (not tested):
select id, count(case reason when 2 then 1 end)/count(*) as proportion
from your_table
-- where ... (if you need to filter, for example by date)
group by id
;
count(*) counts all the rows in a group (that is, all the rows for each separate id). The case expression returns 1 when the reason is 2 and it returns null otherwise; count counts only non-null values, so it will count the rows where the reason is 2.
You can use avg():
select id,
avg(case when reason = 2 then 1.0 else 0 end)
from "DTP"
group by "ID"
This produces the ratio for each id -- based on your sample query. If you only want one row for all the data, then:
select avg(case when reason = 2 then 1.0 else 0 end)
from "DTP";

Use EXISTS in SQL for multiple select

I have a table STATUSES which contain columns NAME and ACTIVE_FLAG.The column value of NAME may have new, pending, cancel. I want to generate a new output for the count of each NAME with ACTIVE_FLAG=Y
By thinking to use EXISTS to select records for single NAME,
SELECT COUNT(*) AS PENDING
FROM STATUSES
WHERE EXISTS (select NAME from STATUSES where NAME='Pending' and ACTIVE_FLAG = 'Y')
Anyway if I can join other statuses count in a single SQL?
Seems like count and group by
SELECT
name
, count(*)
FROM statuses
WHERE active_flag = 'Y'
GROUP BY name
You can use something like this as i don't see any need to use EXISTS :
SELECT sum(case when name='Pending' then 1 else 0 end) AS PENDING,
sum(case when name='new' then 1 else 0 end) AS NEW,
sum(case when name='cancel' then 1 else 0 end) AS CANCEL
FROM STATUSES
WHERE ACTIVE_FLAG = 'Y'
SQL HERE

SQL Multiple Rows to Single Row Multiple Columns

I am including a SQLFiddle to show as an example of where I am currently at. In the example image you can see that simply grouping you get up to two lines per user depending on their status and how many of those statuses they have.
http://sqlfiddle.com/#!3/9aa649/2
The way I want it to come out is to look like the image below. Having a single line per user with two totaling columns one for Fail Total and one for Pass Total. I have been able to come close but since BOB only has Fails and not Passes this query leaves BOB out of the results. which I want to show BOB as well with his 6 Fail and 0 Pass
select a.PersonID,a.Name,a.Totals as FailTotal,b.Totals as PassTotals from (
select PersonID,Name,Status, COUNT(*) as Totals from UserReport
where Status = 'Fail'
group by PersonID,Name,Status) a
join
(
select PersonID,Name,Status, COUNT(*) as Totals from UserReport
where Status = 'Pass'
group by PersonID,Name,Status) b
on a.PersonID=b.PersonID
The below picture is what I want it to look like. Here is another SQL Fiddle that shows the above query in action
http://sqlfiddle.com/#!3/9aa649/13
Use conditional aggregation if the number of values for status column is fixed.
Fiddle
select PersonID,Name,
sum(case when "status" = 'Fail' then 1 else 0 end) as failedtotal,
sum(case when "status" = 'Pass' then 1 else 0 end) as passedtotals
from UserReport
group by PersonID,Name
Use conditional aggregation:
select PersonID, Name,
sum(case when Status = 'Fail' then 1 else 0 end) as FailedTotal,
sum(case when Status = 'Pass' then 1 else 0 end) as PassedTotal
from UserReport
group by PersonID, Name;
With conditional aggregation:
select PersonID,
Name,
sum(case when Status = 'Fail' then 1 end) as Failed,
sum(case when Status = 'Passed' then 1 end) as Passed
from UserReport
group by PersonID, Name