SQL Server - group by ID if column contains a value - sql

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.

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

SQL query for fetching a single column with multiple values

Consider the below table:
Table1
id | status
------------
1 | A
2 | B
3 | C
1 | B
4 | B
5 | C
4 | A
Desired output is 1 and 4 as they are having status as both 'A' and 'B'.
Can we write an query for this? I tried to query it using conditions like 'AND', 'UNION' and 'OR', but it did not return me the desired result.
If you want the ids with more than 1 statuses:
select id
from tablename
group by id
having count(distinct status) > 1
You can use aggregation:
select id
from t
where status in ('A', 'B')
group by id
having count(*) = 2;
If the table allows duplicates, then use count(distinct status) = 2.
Try this one, you can do without using having() as well
select
id
from
(
select
id,
count(distinct status) as cnt
from yourTable
group by
id
) val
where cnt > 1

Select ALL records with fieldvalue1 if any records (with fieldvalue1) have fieldvalue3 have a specific value

I need to show all records for a specific value if ANY one of those records have another specific value. Essentially, if field3 = 'b', what is field1? Show all records with value of field1 regardless of their field3 value.
Record Number External Id Letter
1 123456 a
2 123456 b
3 123456 c
4 456852 t
5 456852 b
record 2 has a letter value of 'b' - so I want to look at externalid, which is 123456, now I want to pull all records for external id regardless if the other records have a letter value of 'b'
Use EXISTS and a correlated subquery:
SELECT *
FROM mytable t
WHERE
t.letter = 'b'
OR EXISTS (
SELECT 1
FROM mytable t1
WHERE
t1.record_number != t.record_number
AND t1.external_id = t.external_id
AND t1.letter = 'b'
)
Another option is to use a window function:
SELECT record_number, external_id, letter
FROM (
SELECT
t.*,
MAX(CASE WHEN letter = 'b' THEN 1 END) OVER(PARTITION BY external_id) mx
FROM mytable t
) x WHERE mx = 1
Demo on DB Fiddle:
record_number | external_id | letter
------------: | ----------: | :-----
1 | 123456 | a
2 | 123456 | b
3 | 123456 | c
4 | 456852 | t
5 | 456852 | b
Use exists, but don't worry about filtering in the outer query:
select t.*
from t
where exists (select 1
from t t2
where t2.external_id = t.external_id and t2.letter = 'b'
);
With an index on (external_id, letter), I would expect this to have very good performance.

Improving query that counts distinct values that have particular values in another column

Say I have a table in the format:
| id | category|
|----|---------|
| 10 | A |
| 10 | B |
| 10 | C |
| 2 | C |
I want to count the number of distinct id's that have all three values A, B, and C in the category variable. In this case, the query would return 1 since only for id = 10 is this true.
My intuition is to write the following query to get this value:
SELECT
COUNT(DISTINCT id),
SUM(CASE WHEN category = 'A' THEN 1 else 0 END) AS A,
SUM(CASE WHEN category = 'B' THEN 1 else 0 END) AS B,
SUM(CASE WHEN category = 'C' THEN 1 else 0 END) AS C
FROM
table
GROUP BY
id
HAVING
A >= 1
AND
B >= 1
AND
C >= 1
This feels a bit overwrought though -- is there a simpler way to achieve the desired outcome?
You are close, but you need two levels of aggregation. Assuming no duplicate rows:
SELECT COUNT(*)
FROM (SELECT id
FROM t
WHERE Category IN ('A', 'B', 'C')
GROUP BY id
HAVING COUNT(*) = 3
) t;
I assume this is part of a larger table, your id and categories can appear multiple times and still be distinct due to other fields, and that you know how many categories you're looking for.
SELECT ID, COUNT(ID)
FROM(
SELECT DISTINCT ID, CATEGORY
FROM TABLE)
GROUP BY ID
HAVING COUNT(ID) = 3 --or however many categories you want
Your subquery here removes extraneous info and forces your id to show up once per category. You then count up the number of times it shows up and look up the ones that show up 3 or however many times you want.

Sum two rows with different IDs

I have the following data:
ID | Field A | Quantity
1 | A | 10
2 | B | 20
3 | C | 30
I would like to sum the fields with ids 1 and 3 in a way that result will be:
ID | Field A | Quantity
1 | A | 40
2 | B | 20
Sounds to me more like code manipulation rather than SQL, but still want to try it.
My DMBS is sql-server.
you can try by using case when
select case when id in(1,3) then 'A' else 'B' end as field,sum(Quantity) as Quantity
from tablename group by case when id in(1,3) then 'A' else 'B' end
I think simple aggregation does what you want:
select min(id) as id, min(fieldA) as fieldA, sum(quantity) as quantity
from t
group by (case when id in (1, 3) then 1 else id end);
Not sure what method to use to combine 1,3 ID but you could try case:
select
case when id in (1,3) then 1 else id end
, min("Field A") "Field A"
, sum(quantity) quantity from myTable
group by case when id in (1,3) then 1 else id end
The above uses group by to aggregate the data. In this case it organizes the ID field using some logic to combine 1,3. All other unique ID will have its own group.
Aggregate functions take care of the other fields, including logic to take the min() value for Field A which seems to fit your requirement