Return records that all match and all records where at least one doesn't match - sql

Given a table of exam results, where 1 == PASS and 0 == FAIL
ID Name Test Result
--------------------
1 John MATH 1
2 John ENGL 1
3 Mary MATH 1
4 Mary PSYC 0
EDIT: assume that the name is unique.
I need to get all records for people who
1) passed all tests
2) failed at least one test
So, the 1st query should return John and all his records, and the 2nd query should return Mary and all her records (including the ones with PASS).
I'm trying to do a LEFT OUTER JOIN with itself and compare counts, but don't seem to get a working query.
SELECT * FROM Results R1
LEFT OUTER JOIN Results R2 on R1.ID=R2.ID and R2.Result=1
WHERE ??? count of rows from R1 is compared to count of non-null rows from R2

This is a "poster-child" exercise for the EXISTS clause:
At leasr one failed result:
select * from Results r
where exists (select * from Results rr where rr.Name=r.Name AND Result=0)
All passed:
select * from Results r
where not exists (select * from Results rr where rr.Name=r.Name AND Result=0)
See how these queries work on your data set at sqlfiddle.com.

All passed
SELECT Name FROM Results R1
GROUP BY NAME
HAVING SUM(RESULT) = COUNT(RESULT)
Some failed
SELECT Name FROM Results R1
GROUP BY NAME
HAVING SUM(RESULT) < COUNT(RESULT)
Hope it helps
Edit
All passed
SELECT Name FROM Results R1
GROUP BY NAME
HAVING SUM(1-RESULT) = 0
Some failed
SELECT Name FROM Results R1
GROUP BY NAME
HAVING SUM(1-RESULT) > 0
(This might run faster)

One way
Select Name,
Case failCount When 0 then 'X' Else '' End PassedAll,
Case failCount When 0 then '' Else 'X' End FailedOneOrMore
From (Select name,
Sum(Case Result when 0 Then 1 Else 0 End) failCount
From Results R
Group By Name) Z
to get all the records, just join to this
Select zz.Name, zz.PassedAll, zz.FailedOneOrMore,
r.Test, r.Result
From (Select Name,
Case failCount When 0 then 'X' Else '' End PassedAll,
Case failCount When 0 then '' Else 'X' End FailedOneOrMore
From (Select name,
Sum(Case Result when 0 Then 1 Else 0 End) failCount
From Results R
Group By Name) Z) ZZ
Left Join Results r On r.Name = zz.Name

This query uses a subquery to return all records (pass & fail) for people who have passed at least one of the Tests:
select * from Results where Name in (select Name from Results where Result = '1' group by Name);
Results exclude those who failed to pass any of the tests.

Related

Add custom bool column where data is calculated based on values from linked entities

I have 2 tables: Entity and EntityItem.
EntityItems table has a Reason column which is nullable enum.
I'm trying to write a view that would return some Entititys columns and additionally a boolean column that states whether all corresponding EntityItem.Reason have a non-null value.
The following query returns somewhat what I want:
SELECT EntityItem.Id, COUNT(EntityItem.Reason) As Test
FROM EntityItem
GROUP BY EntityItem.ParentEntityId
ORDER BY Test DESC
Example output:
Id Test
132189 4
132190 2
132197 1
1 0
2 0
3 0
4 0
5 0
6 0
However, when I try to add this to a final query I get duplicated lines for each EntityItem
SELECT [Entity].[Id],
...
(SELECT CASE WHEN (SELECT COUNT([EntityItem].[Reason]) FROM [EntityItem] WHERE [EntityItem].[ParentEntityId] = [Entity].[Id]) = 0
THEN 0
ELSE 1
END) AS Test
FROM [Entity]
...
LEFT JOIN [EntityItem] ON [Entity].[Id] = [EntityItem].[ParentEntityId]
Example output:
Id Test
1 1
1 1
2 0
2 0
2 0
2 0
3 1
3 1
4 0
Question 1: Is my approach correct?
Question 2: Is there a way to remove duplicated lines without DISTINCT?
For your second query you need to aggregate before joining, for example by using outer apply something like:
select e.Id,
case when i.cnt = 0 then 0 else 1 end as Test
from Entity e
outer apply (
select Count(Reason) cnt
from EntityItem i
where i.ParentEntityId = e.Id
)i;
Saying that, since you are always returning a value of 1 if the count is greater than zero you don't actually need to count anything:
select e.Id,
case when exists (
select * from EntityItem i
where i.ParentEntityId = e.Id
)
then 1 else 0 end as Test
from Entity e;

Compare two columns in SQL

I'm new to SQL and have very basic queries in GCP.
Let's consider this table below:
Name
B
C
Arun
1234-5678
1234
Tara
6789 - 7654
6789
Arun
4567
4324
Here, I want to compare column B and C and if they match then give 1 else 0 in column same and else different (which we have to create).
So here the catch:
if column B has 1234-5678 and column C has 1234, then the column should match considering only the number before the "-" in the value.
The output should be :
Name
B
C
same
different
Arun
1234-5678
1234
1
0
Tara
6789 - 7654
6789
1
0
Arun
4567
4324
0
1
Also, I want to count the values of 1 for each values in Name for same and different columns.
So far I've tried this:
SELECT
name,
b,
c ,
if(b = c, 1, 0) as same,
if (b!=c,1,0) as different,
count(same),
count(different)
From Table
using "MySQL" (will work almost same with SQL server as well) here's the possible solution.
Step 1) Setup table
CREATE TABLE Users (
Name varchar(50),
B varchar(50),
C varchar(50)
);
INSERT INTO Users
VALUES
('Arun', '1234-5678', '1234'),
('Tara', '6789-7654', '6789'),
('Arun', '4567', '4324');
Step 2) same & different columns
SELECT
Name, B, C,
CASE WHEN SUBSTRING_INDEX(B, "-", 1) = C THEN 1 ELSE 0 END as same,
CASE WHEN SUBSTRING_INDEX(B, "-", 1) <> C THEN 1 ELSE 0 END as different
FROM
Users
Step 3) Join both results to get total_same & total_different for each user
SELECT
Name,
SUM(CASE WHEN SUBSTRING_INDEX(B, "-", 1) = C THEN 1 ELSE 0 END) as total_same,
SUM(CASE WHEN SUBSTRING_INDEX(B, "-", 1) <> C THEN 1 ELSE 0 END) as total_different
FROM
Users
GROUP BY Name
Reference: SQL Fiddle
For the first step, you will need to SUBSTR the column b.
We start at position 1 and we want 4 characters (only works if there's only 4 characters before the '-').
With table2 as (
select name, b,c, same, different from (select name, b, c, case when (SUBSTR(b,1,4) = c)
then '1' else '0' end as same, case when(SUBSTR(b,1,4)!= c) then '1' else '0' end as different
from Table1
group by name, b,c))
The WITH clause can be used when you have complex query, and if you want to create a temporary table in order to use it after.
The Table2 give you this :
After the WITH clause, you will have the second step, the count of same / different per name :
Select table1.name,count(table2.same+table2.different) as total from table1
join table2 on (table2.name = table1.name and table2.b = table1.b)
group by table1.name;
The output give you the total per name (the name are group by, so in your example you will only have 2 rows, one for Arun with a total of 2 (same + different) and the other one with a total of 1)
So here's the entire code :
with table2 as (
select name, b,c, same, different from (select name, b, c, case when (SUBSTR(b,1,4) = c) then '1' else '0' end as same, case when(SUBSTR(b,1,4)!= c) then '1' else '0' end as different
From Table1
group by name, b,c))
select table1.name, table1.b, table1.c, count(table2.same+table2.different) as total from table1
join table2 on (table2.name = table1.name and table2.b = table1.b)
group by table1.name;

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.

DAX - count attributes for which FACT records do not exist

I am looking for a DAX measure that is equal to If i have in SQL:
SELECT COUNT(NoDataValue WHEN -1 THEN 1 ELSE 0 END)
FROM
(
SELECT Employee.EmployeeID, CASE WHEN FACT.EmployeeID IS NULL
THEN -1
ELSE 1
END as NoDataValue
FROM Employee LEFT OUTER JOIN FACT
On Employee.EmployeeID = FACT.EmployeeID
) X
Essentially i want -1 when there is no data for an employee at the row level but when its aggregated i need count of NoDataValues (How many employees did not have data).
That is working fine at employee level with the measure i created
Annual Independence Compliance No Data:=
  Var NoData=
  SUM ( [NoDataValue] )
RETURN (IF (ISBLANK(NoData) , -1, NoData))
This looks like
But this is not aggregating the counts. I am having trouble of how to do that. This shows up as
I'm not sure if you SQL query is returning what you want but the query should look like below
SELECT SUM(case NoData WHEN -1 THEN 1 ELSE 0 END) as NoDataCount
FROM
(
SELECT Employee.EmployeeID, CASE WHEN FACT.EmployeeID IS NULL
THEN -1
ELSE 1
END as NoData
FROM Employee LEFT OUTER JOIN FACT
On Employee.EmployeeID = FACT.EmployeeID
) X
or
SELECT SUM(IIF(NoData = -1,1,0)) as NoDataCount

SQL Count with multiple conditions then join

Quick one,
I have a table, with the following structure
id lid taken
1 1 0
1 1 0
1 1 1
1 1 1
1 2 1
Pretty simply so far right?
I need to query the taken/available from the lid of 1, which should return
taken available
2 2
I know I can simply do two counts and join them, but is there a more proficient way of doing this rather than two separate queries?
I was looking at the following type of format, but I can not for the life of me get it executed in SQL...
SELECT
COUNT(case taken=1) AS taken,
COUNT(case taken=0) AS available FROM table
WHERE
lid=1
Thank you SO much.
You can do this:
SELECT taken, COUNT(*) AS count
FROM table
WHERE lid = 1
GROUP BY taken
This will return two rows:
taken count
0 2
1 2
Each count corresponds to how many times that particular taken value was seen.
Your query is correct just needs juggling a bit:
SELECT
SUM(case taken WHEN 1 THEN 1 ELSE 0 END) AS taken,
SUM(case taken WHEN 1 THEN 0 ELSE 1 END) AS available FROM table
WHERE
lid=1
Alternatively you could do:
SELECT
SUM(taken) AS taken,
COUNT(id) - SUM(taken) AS available
FROM table
WHERE
lid=1
SELECT
SUM(case WHEN taken=1 THEN 1 ELSE 0 END) AS taken,
SUM(case WHEN taken=0 THEN 1 ELSE 0 END) AS available
FROM table
WHERE lid=1
Weird application of CTE's:
WITH lid AS (
SELECT DISTINCT lid FROM taken
)
, tak AS (
SELECT lid,taken , COUNT(*) AS cnt
FROM taken t0
GROUP BY lid,taken
)
SELECT l.lid
, COALESCE(a0.cnt, 0) AS available
, COALESCE(a1.cnt, 0) AS taken
FROM lid l
LEFT JOIN tak a0 ON a0.lid=l.lid AND a0.taken = 0
LEFT JOIN tak a1 ON a1.lid=l.lid AND a1.taken = 1
WHERE l.lid=1
;