case statement for each row manipulations - sql

Hi I have a table which has an ID and status.One ID can have multiple status but I have to pick status based on the conditions.
If ID 1 has Approved, Later,Modified I should Pick Approved and if the ID has only approved then Pick only Approved.But the case statement I got is not doing per ID.It is changing the overall data based on status.Please advise
select ID,
CASE
WHEN status = 'Approved'
AND status IN(
'Modified',
'Later'
) THEN 'Partial Modified'
WHEN status = 'Approved' THEN 'Approved'
when status IN('Modified','Edited') THEN 'Modified'
else status
END status group by ID,Status

This can be done with an ordering condition in row_number.
select top 1 with ties *
from tbl
order by row_number() over(partition by id order by case when status='Approved' then 1
when status='Modified' then 2
else 3 end)

I think you want an aggregation, so something like this:
(CASE WHEN SUM(CASE WHEN status = 'Approved' THEN 1 ELSE 0 END) > 0
THEN 'Approved'
WHEN SUM(CASE WHEN status = 'Modified' THEN 1 ELSE 0 END) > 0
THEN 'Modified'
ELSE MAX(status)
END)
Of course, you can also do this using window functions:
(CASE WHEN SUM(CASE WHEN status = 'Approved' THEN 1 ELSE 0 END) OVER (PARTITION BY id) > 0
THEN 'Approved'
WHEN SUM(CASE WHEN status = 'Modified' THEN 1 ELSE 0 END) OVER (PARTITION BY id) > 0
THEN 'Modified'
ELSE MAX(status) OVER (PARTITION BY id)
END)

Related

Sort COUNT(CASE WHEN) results

I am taking a database of statuses and creating the statuses as columns in order to count how many records from a network exist in each status. I'd love to sort the results based on the Partnered column DESC, but I can't figure out how or where to do that??
Here's my code:
SELECT type,
COUNT(CASE WHEN status = "NOT_SUBMITTED" THEN storenumber END) AS Not_Submitted,
COUNT(CASE WHEN status = "PARTNERED" THEN storenumber END) AS Partnered,
COUNT(CASE WHEN status = "PENDING" THEN storenumber END) AS Pending,
COUNT(CASE WHEN status = "SUSPENDED" THEN storenumber END) AS Suspended,
COUNT(CASE WHEN status = "REJECTED" THEN storenumber END) AS Rejected,
FROM Programs
GROUP BY 1;
Here are my results so far.
row
type
Not_Submitted
Partnered
Pending
Suspended
Rejected
1
abc
26
473
36
0
374
2
def
2481
3943
797
363
1074
3
ghi
0
1965
0
150
102
4
jkl
1231
1851
0
0
0
You just add ORDER BY Partnered DESC as in below example
SELECT type,
COUNT(CASE WHEN status = "NOT_SUBMITTED" THEN storenumber END) AS Not_Submitted,
COUNT(CASE WHEN status = "PARTNERED" THEN storenumber END) AS Partnered,
COUNT(CASE WHEN status = "PENDING" THEN storenumber END) AS Pending,
COUNT(CASE WHEN status = "SUSPENDED" THEN storenumber END) AS Suspended,
COUNT(CASE WHEN status = "REJECTED" THEN storenumber END) AS Rejected,
FROM Programs
GROUP BY 1
ORDER BY Partnered DESC
Meantime, consider also below option
SELECT type,
COUNTIF(status = "NOT_SUBMITTED") AS Not_Submitted,
COUNTIF(status = "PARTNERED") AS Partnered,
COUNTIF(status = "PENDING") AS Pending,
COUNTIF(status = "SUSPENDED") AS Suspended,
COUNTIF(status = "REJECTED") AS Rejected,
FROM Programs
GROUP BY 1
ORDER BY Partnered DESC
and finally - try below one (it is my preferred option)
SELECT *
FROM (SELECT type, storenumber, status FROM Programs)
PIVOT (
COUNT(DISTINCT storenumber)
FOR status IN ("NOT_SUBMITTED", "PARTNERED", "PENDING", "SUSPENDED", "REJECTED")
)
ORDER BY PARTNERED DESC
Try this:
#standardSQL
WITH table as (
SELECT type,
COUNT(CASE WHEN status = "NOT_SUBMITTED" THEN storenumber END) AS Not_Submitted,
COUNT(CASE WHEN status = "PARTNERED" THEN storenumber END) AS Partnered,
COUNT(CASE WHEN status = "PENDING" THEN storenumber END) AS Pending,
COUNT(CASE WHEN status = "SUSPENDED" THEN storenumber END) AS Suspended,
COUNT(CASE WHEN status = "REJECTED" THEN storenumber END) AS Rejected,
FROM Programs
GROUP BY 1)
SELECT * FROM table ORDER BY Partnered DESC
I put your query in a subquery then querying the subquery to be ordered by Partnered DESC

Combining two aggregate queries into one

For some context, I am making an image browser which is connected to an SQLite database. Within the browser, similar images are grouped into an event (EventId) and each image (MicrosoftId) is labelled with a few tags (name).
I have these two queries on the same table (TagsMSCV) but pulling out different information. Ultimately I need to combine the information in my browser so if it was possible to combine these two queries (maybe with a JOIN?) it would be a lot faster and convenient for me. Both results of these queries share the EventId column.
1st Query ():
SELECT EventId as 'event', count(*) as 'size',
SUM(case when tag_count = 1 then 1 else 0 end) as '1',
SUM(case when tag_count = 2 then 1 else 0 end) as '2',
SUM(case when tag_count = 3 then 1 else 0 end) as '3'
FROM (SELECT EventId, MicrosoftId,
SUM(case when name in ('indoor', 'cluttered', 'screen') then 1 else 0 end) as tag_count
FROM TagsMSCV GROUP BY EventId, MicrosoftId) TagsMSCV
GROUP BY EventId ORDER BY 3 DESC, 2 DESC, 1 DESC
2nd Query
SELECT EventId,
SUM(CASE WHEN name = 'indoor' THEN 1 ELSE 0 END) as indoor,
SUM(CASE WHEN name = 'cluttered' THEN 1 ELSE 0 END) as cluttered,
SUM(CASE WHEN name = 'screen' THEN 1 ELSE 0 END) as screen
FROM TagsMSCV WHERE name IN ('indoor', 'cluttered', 'screen')
GROUP BY EventId
As you can see in both queries I am feeding in the tags 'necktie' 'man', 'male' and getting different information back.
SQL Fiddle Here: https://www.db-fiddle.com/f/f8WNimjmZAj1XXeCj4PHB8/3
You should do this all in one query:
SELECT EventId as event, count(*) as size,
SUM(case when (indoor + cluttered + screen) = 1 then 1 else 0 end) as tc_1,
SUM(case when (indoor + cluttered + screen) = 2 then 1 else 0 end) as tc_2,
SUM(case when (indoor + cluttered + screen) = 3 then 1 else 0 end) as tc_3,
SUM(indoor) as indoor,
SUM(cluttered) as cluttered,
SUM(screen) as screen
FROM (SELECT EventId, MicrosoftId,
SUM(CASE WHEN name = 'indoor' THEN 1 ELSE 0 END) as indoor,
SUM(CASE WHEN name = 'cluttered' THEN 1 ELSE 0 END) as cluttered,
SUM(CASE WHEN name = 'screen' THEN 1 ELSE 0 END) as screen
FROM TagsMSCV
GROUP BY EventId, MicrosoftId
) TagsMSCV
GROUP BY EventId
ORDER BY 3 DESC, 2 DESC, 1 DESC;
You need two aggregations to get the information about the tag counts. There is no need to add more aggregations and joins to the query.
You could use an Inner join subquery
SELECT TagsMSCV.EventId as 'event', count(*) as 'size',
SUM(case when tag_count = 1 then 1 else 0 end) as '1',
SUM(case when tag_count = 2 then 1 else 0 end) as '2',
SUM(case when tag_count = 3 then 1 else 0 end) as '3',
t.necktie,
t.man,
t.male
FROM (
SELECT EventId, MicrosoftId,
SUM(case when name in ('necktie' 'man', 'male') then 1 else 0 end) as tag_count
FROM TagsMSCV GROUP BY EventId, MicrosoftId
) TagsMSCV
INNER JOIN (
SELECT EventId,
SUM(CASE WHEN name = 'necktie' THEN 1 ELSE 0 END) as necktie,
SUM(CASE WHEN name = 'man' THEN 1 ELSE 0 END) as man,
SUM(CASE WHEN name = 'male' THEN 1 ELSE 0 END) as male
FROM TagsMSCV WHERE name IN ('necktie' 'man', 'male')
GROUP BY EventId
) t on t.EventId = TagsMSCV.EventId
GROUP BY TagsMSCV.EventId
ORDER BY 3 DESC, 2 DESC, 1 DESC

How to calculate a Cumulative total using SQL

I have a Tickets table in My database , each Ticket have a status_id (1,2,3)
1: Ticket IN PROGRESS
2: Ticket Out Of time
3: Ticket Closed
I want using SQL to calculate the number of tickets for each status .
Calculate the cumulative total for each Status in a specific Date, I have already a column affectation_Date that contains the date where the status of ticket has been changed .
Use conditional aggregation as
SELECT TicketID,
AffectationDate,
SUM(CASE WHEN StatusID = 1 THEN 1 ELSE 0 END) InProgress,
SUM(CASE WHEN StatusID = 2 THEN 1 ELSE 0 END) OuOfTime,
SUM(CASE WHEN StatusID = 3 THEN 1 ELSE 0 END) Closed,
COUNT(1) Total
FROM Tickets
GROUP BY TicketID,
AffectationDate
ORDER BY TicketID,
AffectationDate;
Or if you want to GROUP BY AffectationDate only
SELECT AffectationDate,
SUM(CASE WHEN StatusID = 1 THEN 1 ELSE 0 END) TotalInProgress,
SUM(CASE WHEN StatusID = 2 THEN 1 ELSE 0 END) TotalOutOfTime,
SUM(CASE WHEN StatusID = 3 THEN 1 ELSE 0 END) TotalClosed,
COUNT(1) TotalStatusThisDate
FROM Tickets
GROUP BY AffectationDate
ORDER BY AffectationDate;
Live Demo
Using conditional counts.
SELECT affectation_Date,
COUNT(CASE WHEN status_id = 1 THEN 1 END) AS TotalInProgress,
COUNT(CASE WHEN status_id = 2 THEN 1 END) AS TotalOutOfTime,
COUNT(CASE WHEN status_id = 3 THEN 1 END) AS TotalClosed
FROM Tickets t
GROUP BY affectation_Date
ORDER BY affectation_Date
you may use the desired filter condition for the date criteria
SELECT COUNT(1), STATUS
FROM tickets
WHERE affectation_Date >= 'someDate'
group by status
Regards
You just need to group by status and count the number of tickets in each group:
select status, count(*) as number
from Tickets
where dt >= '2019-01-01 00:00:00' and dt < '2019-01-02 00:00:00'
group by status
having status >= 1 and status <= 3
This adds the Cumulative Sum to the existing answers:
SELECT AffectationDate,
Sum(CASE WHEN StatusID = 1 THEN 1 ELSE 0 END) AS TotalInProgress,
Sum(CASE WHEN StatusID = 2 THEN 1 ELSE 0 END) AS TotalOutOfTime,
Sum(CASE WHEN StatusID = 3 THEN 1 ELSE 0 END) AS TotalClosed,
Count(*) as TotalStatusThisDate,
Sum(Sum(CASE WHEN StatusID = 1 THEN 1 ELSE 0 END)) Over (ORDER BY AffectationDate) AS cumTotalInProgress,
Sum(Sum(CASE WHEN StatusID = 2 THEN 1 ELSE 0 END)) Over (ORDER BY AffectationDate) AS cumTotalOutOfTime,
Sum(Sum(CASE WHEN StatusID = 3 THEN 1 ELSE 0 END)) Over (ORDER BY AffectationDate) AS cumTotalClosed,
Sum(Count(*)) Over (ORDER BY AffectationDate) AS cumTotalStatusThisDate
FROM Tickets
GROUP BY AffectationDate
ORDER BY AffectationDate;

Exclude records that have sum greater than 1

I have query returning details of customers that are subscribed to channel xyz or all other channels.
To generate this results i am using the following query:
select customerID
,sum(case when channel='xyz' then 1 else 0 end) as 'xyz Count'
,sum(case when channel<>'xyz' then bundle_qty else 0 end) as 'Other'
From temptable
So my Question is, how do i Exclude customers that are subscribed to 2 channels, where one is xyz and one is another channel.
select customerID
,sum(case when channel='xyz' then 1 else 0 end) as 'xyz Count'
,sum(case when channel<>'xyz' then bundle_qty else 0 end) as 'Other'
From temptable
group by customerID
having sum(case when channel= 'xyz' then 1 else 0 end) > 0
and sum(case when channel<>'xyz' then 1 else 0 end) > 0
First, your query is not correct. It needs a group by. Second, you can do what you want using having:
select customerID,
sum(case when channel = 'xyz' then 1 else 0 end) as xyz_Count,
sum(case when channel<>'xyz' then bundle_qty else 0 end) as Other
From temptable
group by customerID
having count(*) = 2 and
sum(case when channel = 'xyz' then 1 else 0 end) = 1;
If customers can subscribe to the same channel multiple times, and you still want only "xyz" and another channel, then:
having count(distinct channel) = 2 and
(min(channel) = 'xyz' or max(channel) = 'xyz')

Order by the result of a division of two counts

I have a query like this one:
SELECT
type,
count(case when STATUS = 'N/A' then 1 end) as NOTAPPLICABLE,
count(case when STATUS = 'Failed' then 1 end) as FAILED,
count(case when STATUS = 'No Run' then 1 end) as NO_RUN,
count(case when STATUS = 'Not Completed' then 1 end) as NOT_COMPLETE,
count(case when STATUS = 'Blocked' then 1 end) as Blocked,
count(case when STATUS = 'Passed' then 1 end) as PASSED,
count(case when STATUS <> 'N/A' then 1 end) as TOTAL
FROM
table
GROUP BY
type
I want to order the results so the rows with the type with the highest percentage of passed is on top.
I though something like:
ORDER BY
"PASSED"/"TOTAL" DESC
But it's not working.
Do you have any idea to achieve this?
Thanks,
You can use expressions in ORDER BY
SELECT
type,
count(case when STATUS = 'N/A' then 1 end) as NOTAPPLICABLE,
count(case when STATUS = 'Failed' then 1 end) as FAILED,
count(case when STATUS = 'No Run' then 1 end) as NO_RUN,
count(case when STATUS = 'Not Completed' then 1 end) as NOT_COMPLETE,
count(case when STATUS = 'Blocked' then 1 end) as Blocked,
count(case when STATUS = 'Passed' then 1 end) as PASSED,
count(case when STATUS <> 'N/A' then 1 end) as TOTAL
FROM
table
GROUP BY
type
ORDER BY
count(case when STATUS = 'Passed' then 1 end)/count(case when STATUS <> 'N/A' then 1 end) desc
But this can produce division by zero exception you have to check if count(case when STATUS <> 'N/A' then 1 end) is not zero.
The other solution is using sub-queries - You enclose your initial query in sub-query and then you can order, limit or filter this sub-query as simple table in SQL
SELECT *
FROM (
SELECT
type,
count(case when STATUS = 'N/A' then 1 end) as NOTAPPLICABLE,
count(case when STATUS = 'Failed' then 1 end) as FAILED,
count(case when STATUS = 'No Run' then 1 end) as NO_RUN,
count(case when STATUS = 'Not Completed' then 1 end) as NOT_COMPLETE,
count(case when STATUS = 'Blocked' then 1 end) as Blocked,
count(case when STATUS = 'Passed' then 1 end) as PASSED,
count(case when STATUS <> 'N/A' then 1 end) as TOTAL
FROM
table
GROUP BY
type
) AS SUB_DATA
ORDER BY PASSED/TOTAL DESC
if you are using PostgreSQL you can use WITH construction (I very like it).
WITH _records as (
SELECT
type,
count(case when STATUS = 'N/A' then 1 end) as NOTAPPLICABLE,
count(case when STATUS = 'Failed' then 1 end) as FAILED,
count(case when STATUS = 'No Run' then 1 end) as NO_RUN,
count(case when STATUS = 'Not Completed' then 1 end) as NOT_COMPLETE,
count(case when STATUS = 'Blocked' then 1 end) as Blocked,
count(case when STATUS = 'Passed' then 1 end) as PASSED,
count(case when STATUS <> 'N/A' then 1 end) as TOTAL
FROM
table
GROUP BY
type
)
SELECT *
FROM _records
ORDER BY PASSED/TOTAL DESC
If Column aliases that are defined in the SELECT are then referenced in the ORDER BY they must be used on their own. Not in an expression.
You can use a derived table.
SELECT *
FROM
(
/* Your Query here*/
) T
ORDER BY PASSED/TOTAL DESC
You may also need to cast PASSED to numeric to avoid integer division depending on your DBMS.
SELECT *, (PASSED / TOTAL) [percent] FROM
( SELECT
type,
count(case when STATUS = 'N/A' then 1 end) as NOTAPPLICABLE,
count(case when STATUS = 'Failed' then 1 end) as FAILED,
count(case when STATUS = 'No Run' then 1 end) as NO_RUN,
count(case when STATUS = 'Not Completed' then 1 end) as NOT_COMPLETE,
count(case when STATUS = 'Blocked' then 1 end) as Blocked,
count(case when STATUS = 'Passed' then 1 end) as PASSED,
count(case when STATUS <> 'N/A' then 1 end) as TOTAL
FROM
table
GROUP BY
type ) T
ORDER BY [percent]
There are two problems in your approach:
As others already pointed out, you cant' use column aliases in
calculation. Instead of ORDER BY PASSED/TOTAL DESC, write ORDER
BY count(case when STATUS = 'Passed' then 1 end) / count(case when
STATUS <> 'N/A' then 1 end)
If you divide PASSED by TOTAL, and PASSED is less than TOTAL, you'll
always get 0 as a result. Just like select 5/10 will return 0
instead of 0.5 - because both values are integers, you'll get integer as a result. select 1.0*5/10 will return 0.5
your code works in sql server, but not in oracle i think. try:
SELECT
type,
count(case when STATUS = 'N/A' then 1 end) as NOTAPPLICABLE,
count(case when STATUS = 'Failed' then 1 end) as FAILED,
count(case when STATUS = 'No Run' then 1 end) as NO_RUN,
count(case when STATUS = 'Not Completed' then 1 end) as NOT_COMPLETE,
count(case when STATUS = 'Blocked' then 1 end) as Blocked,
count(case when STATUS = 'Passed' then 1 end) as PASSED,
count(case when STATUS <> 'N/A' then 1 end) as TOTAL,
count(case when STATUS = 'Passed' then 1 end) / count(case when STATUS <> 'N/A' then 1 end) as sort
FROM
table
GROUP BY
type
ORDER BY 9
sort DESC