SQL - SELECT statement with sub queries - sql

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;

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.

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.

How to COUNT different values without adding to GROUP BY

I have a data set that contains a name for every "job" record, and whether the job passed or failed. I want to show the Name, number of jobs, how many passed, and how many failed in one row.
I am grouping the name and using COUNT on the name to count the total number of jobs, which works fine, but I can't show how many passed and how many failed without adding them to the GROUP BY clause causing the data to separate again.
SELECT I.Name, Count(I.Name) As NumberOfJobs,
CASE WHEN WI.resultTypeID = 1 THEN COUNT(WI.resultTypeID) END AS [Passed],
CASE WHEN WI.resultTypeID = 2 THEN COUNT(WI.resultTypeID) END AS [Failed],
FROM DB.DBO.People AS I
INNER JOIN DB2.dbo.Jobs AS WI ON I.JOBID = WI.JOBID
GROUP BY I.Name, wi.resultTypeID
+-----------+-----------+--------+--------+
| Name | NumofJobs | Passed | Failed |
+-----------+-----------+--------+--------+
| Dale Test | 2 | 2 | NULL |
| Dale Test | 2 | NULL | 2 |
+-----------+-----------+--------+--------+
This is what happens when I add ResultTypeID to the GROUP BY, but I want this:
+-----------+-----------+--------+--------+
| Name | NumofJobs | Passed | Failed |
+-----------+-----------+--------+--------+
| Dale Test | 4 | 2 | 2 |
+-----------+-----------+--------+--------+
Is there anyway to do this?
You want conditional aggregation. The case expression is an argument to the aggregation function:
SELECT I.Name, Count(*) As NumberOfJobs,
SUM(CASE WHEN WI.resultTypeID = 1 THEN 1 ELSE 0 END) AS [Passed],
SUM(CASE WHEN WI.resultTypeID = 2 THEN 1 ELSE 0 END) AS [Failed],
FROM DB.DBO.People I INNER JOIN
DB2.dbo.Jobs WI
ON I.JOBID = WI.JOBID
GROUP BY I.Name;
I am guessing that wi.resultTypeID is not NULL, so I replaced the COUNT() with SUM() because I prefer SUM() in this case.
You don't need to group your query by wi.resultTypeID .
simply remove wi.resultTypeID from group by statement and put it inside aggregate function:
SELECT I.Name, Count(I.Name) As NumberOfJobs,
SUM(CASE WHEN WI.resultTypeID = 1 THEN 1 ELSE 0 END) AS [Passed],
SUM(CASE WHEN WI.resultTypeID = 2 THEN 1 ELSE 0 END) AS [Failed],
FROM DB.DBO.People AS I
INNER JOIN DB2.dbo.Jobs AS WI ON I.JOBID = WI.JOBID
GROUP BY I.Name

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.

get All distinct (by ID) Users list and count only thoose who is Active

I need to get All distinct User list and count only thoose who is Active
and get something like
ID | Count | Active
1 | 0 | False
2 | 1 | True
3 | 6 | True
but when i do
SELECT ID,COUNT(*)
FROM Users
WHERE Active='True'
GROUP BY ID
I got All Distinct Which Active
ID | Count | Active
2 | 1 | True
3 | 6 | True
How to write query to get what i want?
You want to remove the where clause. If active only takes on the values True and False (and NULL), then this will work:
SELECT ID, COUNT(*) as cnt, MAX(Active)
FROM Users
GROUP BY ID;
If Active can take on other values, then you might want:
SELECT ID, COUNT(*) as cnt,
(CASE WHEN SUM(CASE WHEN Active = 'True' THEN 1 ELSE 0 END) > 0 THEN 'True' ELSE 'False' END) as Active
FROM Users
GROUP BY ID;
You need to move the condition into the aggregate:
SELECT ID, SUM(CASE WHEN Active='True' THEN 1 ELSE 0 END)
FROM Users
GROUP BY ID
SELECT ID, COUNT(*) as count,
(CASE WHEN count > 0 THEN 'True' ELSE 'False' END) as Active
FROM test
GROUP BY ID,count
The output will come as below:
ID count Active
1 1 False
2 1 True
3 1 True