SQL - group by column, count into a single row - sql

I'm trying take the result from a group by <col> into a single row, as my graphing library requires a single row for visualizing a stacked bar. For context, I'm using Presto/AWS Athena.
My query is SELECT result, count(*) FROM table GROUP BY result
Essentially
result | count
skipped | 12
passed | 13
failed | 2
Into
skipped | passed | failed
12 | 13 | 2

For a small set like this, conditional aggregation is simplest.
select
max (case when result = 'skipped' then cnt end) as skipped,
max(case when result = 'passed' then cnt end) as passed,
max(case when result = 'failed' then cnt end) as failed
from
(SELECT result, count(*) cnt FROM table GROUP BY result) t

Related

Calculate difference in Oracle table from sum

I have a table which looks as followed:
ID | Value
A | 2
A | 5
A | 6
B | 1
B | 7
B | -3
I am currently using a statement as followed
select ID, sum(VALUE)
where ...
group by ID.
Now I need the difference from A and B.
Could anyone send me on the right path? I am working with Oracle.
Use conditional aggregation:
SELECT SUM(CASE WHEN id = 'A' THEN "Value" ELSE 0 END) -
SUM(CASE WHEN id = 'B' THEN "Value" ELSE 0 END) "Difference"
FROM tablename;
See the demo.

SQL - SELECT statement with sub queries

Lets take below table as sample
ID Name Status
1 Jon pass
2 Jon fail
3 Jon fail
4 Snow pass
5 Snow fail
6 Snow fail
I need to write a query that displays results in the below format
Name Total Pass Fail
Jon 3 1 2
Snow 3 1 2
I am trying the following query with subquery in select but I know its not correct. Please advise.
SELECT
Name,
count(ID) as Total,
(SELECT count(ID) FROM results WHERE status = 'pass') as Pass
(SELECT count(ID) FROM results WHERE status = 'fail') as Fail
FROM results
HAVING count(ID)>2
GROUP BY Name
ORDER BY count(ID) desc;
You can do it with conditional aggregation:
SELECT
Name,
COUNT(ID) as Total,
COUNT(CASE WHEN status = 'pass' THEN 1 END) Pass,
COUNT(CASE WHEN status = 'fail' THEN 1 END) Fail
FROM results
GROUP BY Name
HAVING COUNT(ID) > 2
ORDER BY COUNT(ID) desc;
I kept the HAVING clause (which must be placed after GROUP BY) because you use it in your code.
See the demo.
Results:
> NAME | TOTAL | PASS | FAIL
> :--- | ----: | ---: | ---:
> Jon | 3 | 1 | 2
> Snow | 3 | 1 | 2
Try this below script-
SELECT
Name,
count(ID) as Total,
SUM(CASE WHEN status = 'pass' THEN 1 ELSE 0 END) as Pass,
SUM(CASE WHEN status = 'fail' THEN 1 ELSE 0 END) as fail
FROM results
GROUP BY Name
HAVING count(ID)>2
ORDER BY count(ID) desc;

How to include row totals in pivot statement in Oracle?

I have a table of data, see
Using a pivot statement, I am able to break down the count by title
select * from (
select * from ta
)
pivot (
COUNT(title)
for title in ( 'worker', 'manager') )
So the result looks like this:
STATUS 'worker' 'manager'
started 3 1
finished 4 5
ready 3 4
What I need to add a third column for the row totals
STATUS 'worker' 'manager' Total
started 3 1 4
finished 4 5 9
ready 3 4 7
Any idea how I can accomplish this within the same statement?
demo is at http://sqlfiddle.com/#!4/740fd/1
I would just use conditional aggregation rather than pivot. This gives you the extra flexibility that you need:
select
status,
sum(case when title = 'worker' then 1 else 0 end) worker,
sum(case when title = 'manager' then 1 else 0 end) manager,
count(*) total
from ta
group by status
Demo on DB Fiddle:
STATUS | WORKER | MANAGER | TOTAL
:------- | -----: | ------: | ----:
started | 3 | 1 | 4
finished | 4 | 5 | 9
ready | 3 | 4 | 7
Use the SUM() analytic function to get the total and then use PIVOT
select
status,
sum(case
when title = 'worker'
then 1
else 0
end) worker,
sum(case
when title = 'manager'
then 1
else 0
end) manager,
count(*) total
from ta
group by status
Give an alias for the whole query(such as q) in order to qualify the all columns with asterisk(q.*), and then sum up all the columns to yield total column next to it :
select q.*, worker + manager as total
from ta
pivot
(
count(title)
for title in ( 'worker' as worker, 'manager' as manager )
) q
Demo
I think the other examples are much simpler, but here is a different approach using cube and grouping before pivoting:
select *
from (
select decode(grouping(title),1,'total',0,title) title,
status,
count(*) cnt
from ta
group by status, cube(title) )
pivot(
sum(cnt) for title in ('worker','manager','total')
)
Output:
| STATUS | 'worker' | 'manager' | 'total' |
|----------|----------|-----------|---------|
| finished | 4 | 5 | 9 |
| ready | 3 | 4 | 7 |
| started | 3 | 1 | 4 |
http://sqlfiddle.com/#!4/740fd/13/0
Adding the cube into the group by clause will give you a subtotal for that column. It will show as null in that column by default. You can use the grouping function in the select clause to differentiate between the total row and the normal rows (the total row will be 1, normal rows are 0). Using a decode will force those total rows to be 'total' which becomes one of the values that you can pivot on.

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.

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.