Simple group-by for SQL pull - sql

I have the following table:
Check | Email | Count
Y | a | 1
Y | a | 1
Y | b | 1
N | c | 1
N | d | 1
I want to group it by 'check' and number of counts under each email. So like this:
Check | Count # | Email Addresses
Y | 1 count | 1 (refers to email b)
Y | 2+ counts | 1 (refers to email a)
N | 1 count | 2 (refers to email c & d)
N | 2+ counts | 0 (no emails meet this condition)
Every 'check' value is specific to an email

This is most easily done by putting the values in columns not rows.
But it requires two levels of aggregation:
select check, sum(case when cnt = 1 then 1 else 0 end) as cnt_1,
sum(case when cnt >= 2 then 1 else 0 end) as cnt_2plus
from (select check, email, count(*) as cnt
from t
group by check, email
) ce
group by check;

This should work, but there might be a cleaner way to get there. I think you need an extra layer of aggregation to pick up the cases where no email meets the condition, assuming you have a record in the source table where the email is null. If there's no record of these cases in the source table, this won't work.
select check
,count_num
,case when email_addresses is null then 0 else email_addresses end as email_addresses
from (
select check,
case when count_sum = 1 then 1 when count_sum > 1 then 2+ else 0 end as count_num,
count(distinct(email)) as email_addresses
group by check, count_num
from (
select check, sum(count) as count_sum, email
from table
group by check, email
)
)

Related

Oracle SQL: Dividing Counts into unique and non unique columns

I have a table that looks like this:
|FileID| File Info |
| ---- | ------------ |
| 1 | X |
| 1 | Y |
| 2 | Y |
| 2 | Z |
| 2 | A |
I want to aggregate by FileID and split the File Info column into 2 separate count columns. I want 1 column to have the count of the Unique File Info and the other to be a count of non-Unique file info.
The result would ideally look like this:
|FileID| Count(Unique)| Count(Non-unique) |
| ---- | ------------ | ----------------- |
| 1 | 1 | 1 |
| 2 | 2 | 1 |
where the non-unique count is the 'Y' and the unique count is from the 'X' and 'Z, A' for FileID 1 and 2 respectively.
I'm looking for ways to gauge uniqueness between files rather than within.
Use COUNT() window function in every row to check if FileInfo is unique and then use conditional aggregation to get the results that you want:
SELECT FileID,
COUNT(CASE WHEN counter = 1 THEN 1 END) count_unique,
COUNT(CASE WHEN counter > 1 THEN 1 END) count_non_unique
FROM (
SELECT t.*, COUNT(*) OVER (PARTITION BY t.FileInfo) counter
FROM tablename t
) t
GROUP BY FileID;
See the demo.
First you select the "Non Unique" rows from the table
SELECT FileInfo
FROM sometableyoudidnotname
GROUP BY FileInfo
HAVING COUNT(*) > 1
Now that you know which ones are unique and non unique you can left join to that table to get the "status" and count it up.
SELECT base.FileID,
SUM(CASE WHEN u.FileID is NOT NULL THEN 1 ELSE 0 END) as nonunique,
SUM(CASE WHEN u.FileID is NULL THEN 1 ELSE 0 END) as unique
FROM sometableyoudidnotname base
LEFT JOIN (
SELECT FileInfo
FROM sometableyoudidnotname
GROUP BY FileInfo
HAVING COUNT(*) > 1
) u ON base.FileInfo = u.FileInfo
GROUP BY base.FileID
Have a derived table that counts occurrences of each fileid. JOIN and GROUP BY:
select t1.FileID,
sum(case when t2.ficount = 1 then 1 else 0 end),
sum(case when t2.ficount > 1 then 1 else 0 end)
from tablename t1
join
(
select fileinfo, count(*) ficount
from tablename
group by fileinfo
) t2
on t1.fileinfo = t2.fileinfo
group by t1.FileID

How to return records from a subquery where the row count of the subquery is equal to X?

I have a table named Groups structured like so...
+--------------+
| Id GroupId |
+--------------+
| 1 3 |
| 2 3 |
| 3 2 |
| 1 2 |
| 2 2 |
| 3 2 |
+--------------+
I want to return the GroupId where Id = 1 and the other Id = 2, so the result should be 3. Here's what I've tried so far...
SELECT GroupId FROM Groups G1
WHERE G1.Id = 1 and exists ( select 1
FROM Groups G2
WHERE G2.Id = 2
and G1.GroupId = G2.GroupId)
This works fine until a group is added where both Ids exist in (group 2). Then, this fails as the subquery returned more than 1 value.
I've thought about using HAVING COUNT(*) == 2 to try and get the subquery to return the group with only 2 row counts but I'm not sure how to do that, any ideas?
Use group by and having:
select groupid
from groups
where id in (1, 2)
group by groupid
having count(*) = 2;
This assumes that the rows are unique. If you can have duplicates, use count(distinct id) = 2.
If you want 1 & 2 and no other ids, the logic is slightly more complicated:
select groupid
from groups
group by groupid
having sum(case when id = 1 then 1 else 0 end) > 0 and
sum(case when id = 2 then 1 else 0 end) > 0 and
count(*) = 2;

SQL Server - group by ID if column contains a value

I have following table:
ID | NR | Status
1000 | 1 | A
1000 | 2 | A
1001 | 3 | A
1002 | 4 | A
1002 | 5 | N
1003 | 6 | N
I need to an output which groups these by ID's. The NR column can be ignored. If one of the records with those ID's contains Status A, That status will be given as result.
So my output would be:
ID | Status
1000 | A
1001 | A
1002 | A
1003 | N
Any suggestions/ideas?
Although min() is the simplest method, it is not easily generalizable. Another method is:
select id
(case when sum(case when status = 'A' then 1 else 0 end) > 0
then 'A'
else 'N' -- or whatever
end) as status
from t
group by id;
Or, if you have a table with one row per id, then I would use exists:
select ids.id,
(case when exists (select 1 from t where t.id = ids.id and t.status = 'A')
then 'A' else 'N'
end) as status
from ids;
This saves on the group by aggregation and can use an index on (id, status) for optimal performance.
Do a GROUP BY, use MIN() to pick minimum status value for each id, and A < N!
select id, min(status)
from tablename
group by id
You want exactly the records that match the predicate "If one of the records with those ID's contains Status A, that status will be given as result." ?
The query can be written simply as:
Select distinct ID, STATUS from [your working TABLE] where STATUS = 'A'.
Hope this can help.

SQL : Conditional query on several rows

I have a table like
letter | number
a | 1
a | 1
b | 2
c | 1
c | 2
c | 2
and I would like to write a SQL query that only returns rows corresponding to letter values that are associated with both a number '1' and a number '2', i.e. I want to keep only
c | 1
c | 2
c | 2
from my example above.
Can anyone help? Many thanks!
You need to use Group By and Having clause
This will give you the letters that are associated to number 1 and 2
SELECT *
FROM yourtable
WHERE letter IN (SELECT letter
FROM yourtable
WHERE number IN ( 1, 2 )
GROUP BY letter
HAVING Count(DISTINCT number) = 2)
SQLFIDDLE DEMO
If you want to find the letters that are associated only to 1 and 2 then use this
SELECT letter
FROM test
GROUP BY letter
HAVING Count(DISTINCT CASE WHEN number = 1 THEN 1 END) = 1
AND Count(DISTINCT CASE WHEN number = 2 THEN 1 END) = 1
AND Count(DISTINCT number) = 2
SQLFIDDLE DEMO

Search for records with same value in one column but varying values in a another

Apologies for my very ambiguous title, but i've been working on this for the better part of a day and can't get anywhere so i'm probably clouded.. Let me present sample data and explain what I'm trying to do:
+------+------+
| ID | UW |
+------+------+
| 1 | I |
| 1 | I |
| 3 | I |
| 3 | I |
| 3 | C |
| 3 | C |
| 4 | C |
| 4 | C |
I'm trying to find the count of IDs where there are both "I" and "C" in the UW column, so in the example above the count would be: 1 (for ID #3). Since ID 1 has only "I" and ID 4 has only "C" values in "UW" field. Thanks in advance for helping me with this, much appreciated.
Here is one way:
SELECT COUNT(DISTINCT A.ID) N
FROM dbo.YourTable A
WHERE EXISTS(SELECT 1 FROM dbo.YourTable
WHERE ID = A.ID
AND UW IN ('I','C'));
And another:
SELECT COUNT(*)
FROM ( SELECT ID
FROM dbo.YourTable
WHERE UW IN ('I','C')
GROUP BY ID
HAVING COUNT(DISTINCT UW) = 2) A;
You can use group by and having to get the ids that meet the conditions:
select id
from table t
group by id
having sum(case when uw = 'I' then 1 else 0 end) > 0 and
sum(case when uw = 'C' then 1 else 0 end) > 0;
You can then count these with a subquery:
select count(*)
from (select id
from table t
group by id
having sum(case when uw = 'I' then 1 else 0 end) > 0 and
sum(case when uw = 'C' then 1 else 0 end) > 0
) t
I like to formulate these problems this way, because the having clause is very general on the types of conditions that it can support.