SELECT DISTINCT HAVING Count unique conditions - sql

I've searched for an answer on this but can't find quite how to get this distinct recordset based on a condition. I have a table with the following sample data:
+---------+-------------+
| Branch | Task Status |
+---------+-------------+
| Account | Completed |
| HR | Completed |
| Account | Completed |
| HR | Not Define |
| Account | Uncompleted |
| Account | Not Define |
| Account | Completed |
| HR | Uncompleted |
| HR | Uncompleted |
| HR | Completed |
| HR | Not Define |
+---------+-------------+
I'd like to create a query that shows the count of total task and also want total of Task Status wise numbers of tasks, e.g.
Branch Total Task Completed Uncompleted Not Define
Account 5 3 1 1
Admin 6 2 2 2

SELECT branch,
COUNT(*),
SUM(CASE status WHEN 'completed' THEN 1 END) AS completed,
SUM(CASE status WHEN 'uncompleted' THEN 1 END) AS uncompleted,
SUM(CASE status WHEN 'not define' THEN 1 END) AS not_define
FROM task
GROUP BY
branch

SELECT Branch
, COUNT(1) as Total
, COUNT(CASE WHEN TaskStatus = 'Completed' THEN 1 ELSE 0 END) AS Completed
, COUNT(CASE WHEN TaskStatus = 'Uncompleted' THEN 1 ELSE 0 END) AS Uncompleted
, COUNT(CASE WHEN TaskStatus = 'Not Define' THEN 1 ELSE 0 END) AS NotDefine
FROM Table
GROUP BY Branch

SELECT branch,
COUNT(*),
COUNT(CASE taskstatus WHEN 'Completed' THEN 1 END),
COUNT(CASE taskstatus WHEN 'Uncompleted' THEN 1 END),
COUNT(CASE taskstatus WHEN 'Not Define' THEN 1 END)
FROM YourTable
GROUP BY branch;

Try this
SELECT * FROM (SELECT
ROW_NUMBER() OVER(PARTITION BY Branch ORDER BY [Task Status]) AS No,
Branch,
sum (CASE WHEN [Task Status] = 'Completed' THEN 1 ELSE 0 END ) OVER (PARTITION BY Branch) AS [Completed],
sum (CASE WHEN [Task Status] = 'Not Define' THEN 1 ELSE 0 END ) OVER (PARTITION BY Branch) AS [Not Define],
sum (CASE WHEN [Task Status] = 'Uncompleted' THEN 1 ELSE 0 END ) OVER (PARTITION BY Branch) AS [Uncompleted]
FROM task ) AS T1 WHERE No = 1
or
SELECT * FROM (SELECT
ROW_NUMBER() OVER(PARTITION BY Branch ORDER BY [Task Status]) AS No,
Branch,
count (CASE WHEN [Task Status] = 'Completed' THEN 1 ELSE 0 END ) OVER (PARTITION BY Branch) AS [Completed],
count (CASE WHEN [Task Status] = 'Not Define' THEN 1 ELSE 0 END ) OVER (PARTITION BY Branch) AS [Not Define],
count (CASE WHEN [Task Status] = 'Uncompleted' THEN 1 ELSE 0 END ) OVER (PARTITION BY Branch) AS [Uncompleted]
FROM task ) AS T1 WHERE No = 1

In SQL Server2005+ you can use PIVOT operator
SELECT *
FROM (
SELECT *, COUNT(*) OVER(PARTITION BY Branch) AS TotalTask
FROM dbo.task
) p
PIVOT
(
COUNT(p.Task) FOR p.Task IN ([Completed], [Uncompleted], [Not Define])
) x
Demo on SQLFiddle

Related

Sum rows with same Id based on type and exlude where SUM = 0

I have this table MOVEMENTS:
Id | FatherId | MovementType | Quantity |
=================================================
1 | A | IN | 10 |
2 | A | IN | 5 |
3 | A | OUT | 5 |
4 | B | IN | 10 |
5 | B | OUT | 10 |
6 | C | IN | 5 |
I'm trying to get all the FatherId with the SUM of IN - OUT Movments > 0.
So the result would be:
FatherId | Total |
=========================
A | 10 |
C | 5 |
FatherId = B not showing because
SUM(MovementType = IN) - SUM (MovementType = OUT) = 0
I tried with
SELECT FatherId,
(SELECT (
SUM(CASE WHEN MovementType = 'IN' THEN Quantity ELSE 0 END) -
SUM(CASE WHEN MovementType = 'OUT' THEN Quantity ELSE 0 END)
)) AS Total
FROM MOVEMENTS
GROUP BY FatherId
ORDER BY FatherId
That gives me the result grouped by FatherId, but I'm not able to filter with Total > 0, and also, I'm unable to put this query in a Subquery like:
SELECT * FROM MOVEMENTS WHERE FatherId IN (SELECT ....) OFFSET ... FETCH NEXT ... ROWS ONLY
Is this doable without a stored procedure?
Thank you for any help
Why are you using a subquery? This should do what you want:
SELECT FatherId,
(SUM(CASE WHEN MovementType = 'IN' THEN Quantity ELSE 0 END) -
SUM(CASE WHEN MovementType = 'OUT' THEN Quantity ELSE 0 END)
) AS Total
FROM MOVEMENTS
GROUP BY FatherId
HAVING (SUM(CASE WHEN MovementType = 'IN' THEN Quantity ELSE 0 END) -
SUM(CASE WHEN MovementType = 'OUT' THEN Quantity ELSE 0 END)
) > 0;
You can also simplify the logic to use a single SUM():
SELECT FatherId,
SUM(CASE WHEN MovementType = 'IN' THEN Quantity
WHEN MovementType = 'OUT' THEN - Quantity
ELSE 0
END) AS Total
FROM MOVEMENTS
GROUP BY FatherId
HAVING SUM(CASE WHEN MovementType = 'IN' THEN Quantity
WHEN MovementType = 'OUT' THEN - Quantity
ELSE 0
END) > 0
ORDER BY FatherId;

Conditional count when case = 1, DB2

I'm currently trying to figure out the best way to do a conditional count as an alias in DB2 for Iseries. The below values represent job statuses where a job can be created, completed and cancelled so any one job will possibly have multiple status codes attached to it.
However, for my final value, I'm trying to get a count of jobs that only have the created status so that I can show how many are still open jobs. Basically looking for cases where the count for the created case = 1, but the below fails at the '='
SELECT
COUNT(CASE A1.JOB WHEN = 'CREATED' THEN 1 END) AS CREATED,
COUNT(CASE A1.JOB WHEN = 'CANCELLED' THEN 1 END) AS CANCELLED,
COUNT(CASE WHEN A1.JOB 'CREATED' = 1 then 1 END) AS OPEN
FROM SCHEMA.TABLE A1;
sample data and results:
Job ID | Status_code
-------------------------
123 'CREATED'
123 'COMPLETED'
521 'CREATED'
521 'CANCELLED'
645 'CREATED'
Results:
JOB | CREATED | CANCELLED | OPEN
-------------------------------------------
123 1 0 0
521 1 1 0
645 1 0 1
Assuming that the only "close" status is 'CANCELLED', you can use not exists like this:
select count(*)
from schema.table t
where t.status_code = 'CREATED' and
not exists (select 1
from schema.table t2
where t2.job = t.job and
t2.status_code in ('CANCELLED', 'COMPLETED', 'DELETED')
);
If you want multiple counts, then filtering like this does not work. So aggregate by job first:
select sum(is_created) as num_created,
sum(is_cancelled) as num_cancelled,
sum(is_created * (1 - is_cancelled) * (1 - is_completed) * (1 - is_deleted)) as open
from (select job,
max(case when status_code = 'CREATED' then 1 else 0 end) as is_created,
max(case when status_code = 'CANCELLED' then 1 else 0 end) as is_cancelled,
max(case when status_code = 'COMPLETED' then 1 else 0 end) as is_completed,
max(case when status_code = 'DELETED' then 1 else 0 end) as is_deleted
from t
group by job
) j
The following returns the result you need:
WITH TAB (Job_ID, JOB) AS
(
VALUES
(123, 'CREATED')
, (123, 'COMPLETED')
, (521, 'CREATED')
, (521, 'CANCELLED')
, (645, 'CREATED')
)
SELECT
Job_ID
, COUNT(CASE A1.JOB WHEN 'CREATED' THEN 1 END) AS CREATED
, COUNT(CASE A1.JOB WHEN 'CANCELLED' THEN 1 END) AS CANCELLED
, CASE
WHEN NULLIF(COUNT(1), 0) = COUNT(CASE A1.JOB WHEN 'CREATED' then 1 END)
THEN 1
ELSE 0
END AS OPEN
FROM TAB A1
GROUP BY JOB_ID;
With conditional aggregation:
SELECT
JobID,
MAX(CASE Status_code WHEN 'CREATED' THEN 1 ELSE 0 END) AS CREATED,
MAX(CASE Status_code WHEN 'CANCELLED' THEN 1 ELSE 0 END) AS CANCELLED,
MIN(CASE WHEN Status_code <> 'CREATED' THEN 0 ELSE 1 END) AS OPEN
FROM tablename
GROUP BY JobID
See the demo.
Results:
> JobID | CREATED | CANCELLED | OPEN
> ----: | ------: | --------: | ---:
> 123 | 1 | 0 | 0
> 521 | 1 | 1 | 0
> 645 | 1 | 0 | 1
Assuming valid close status is either "COMPLETED" or "CANCELLED", you can try following SQL.
SELECT
A1.JobID,
sum(CASE WHEN A1.Status_code = 'CREATED' THEN 1 ELSE 0 END) AS CREATED,
sum(CASE WHEN A1.Status_code = 'CANCELLED' THEN 1 ELSE 0 END) AS CANCELLED,
(
SUM(CASE WHEN A1.Status_code = 'CREATED' THEN 1 ELSE 0 END)
- sum(CASE WHEN A1.Status_code = 'CANCELLED' THEN 1 ELSE 0 END)
- sum(CASE WHEN A1.Status_code = 'COMPLETED' THEN 1 ELSE 0 END)
) AS OPEN
FROM SCHEMA.TABLE A1
GROUP BY A1.JobID

Get result in single row

I am querying on table below.
SELECT [RiskUniqueIdentificationNo]
,Count(*) AS TotalNoOfTasks
,[Status]
FROM [dbo].[Tasks]
GROUP BY [RiskUniqueIdentificationNo]
,[Status]
and here is my result set
but problem is that i want to get result for each [RiskUniqueIdentificationNo] in following way
| RiskUniqueIdentificationNo | | TotalNoOfTasks | | InProgressTask | | ClosedTask |
------------ ------------ ------------ ------------------------- ------------------
| 1 | | 3 | | 2 | | 1|
i have tried to do some thing like this
SELECT [RiskUniqueIdentificationNo]
,Count(*) AS TotalNoOfTasks
,CASE
WHEN [Status] = 'Closed'
THEN COUNT([Status])
ELSE 0
END AS ClosedTasks
,CASE
WHEN [Status] != 'Closed'
AND [EndDate] < GETDATE()
THEN COUNT([Status])
ELSE 0
END AS OverdueTasks
FROM [dbo].[Tasks]
GROUP BY [RiskUniqueIdentificationNo]
,[Status]
but can't succeeded. I need help that how can i accomplish this.
Remove status from GROUP BY and do a COUNT using CASE.
SELECT [RiskUniqueIdentificationNo],
COUNT(1) AS TotalNoOfTasks,
COUNT(CASE WHEN [Status]<> 'Closed' THEN 1 END) AS InProgressTask,
COUNT(CASE WHEN [Status]= 'Closed' THEN 1 END) AS ClosedTask
FROM [dbo].[Tasks]
GROUP BY [RiskUniqueIdentificationNo]
Move to conditional aggregation as below
Select [RiskUniqueIdentificationNo],
Count(*) as TotalNoOfTasks,
sum(case when [Status] = 'In progress' then 1 else 0 end) [InProgressTask],
sum(case when [Status] = 'Closed' then 1 else 0 end) [ClosedTask]
from [dbo].[Tasks]
group by [RiskUniqueIdentificationNo]

Select As Status If Subtable Lines Include Value

I have 2 Table named Order and OrderDetails
Order Table
orderID orderDate OrderUserId
1 10 01.01.2010 .....
2 20 05.05.2011 .....
OrderDetails Table
DetailID OrderIdOfDetail DetailProductID DetailStatusID
1 25 10 xxx 1
2 26 10 xxx 2
3 27 10 xxx 2
4 28 10 xxx 0
5 29 10 xxx 1
6 30 20 xxx 1
7 31 20 xxx 2
8 32 20 xxx 0
DetailStatusId for closed , pending, cancelled bla...
For example I want to select orderID : 10.
If detail lines include DetailsStatusID 1 select order Status as "Pending".
If detail lines include DetailsStatusID 2 select order Status as "Open".
If all DetailsStatusID is equal and 0 select order status as "Closed"
I tried "if exist" but couldn't success.
How can I do it ?
You can use a query like the following:
SELECT o.orderID,
CASE
WHEN MAX(CASE WHEN DetailStatusID = 1 THEN 1 ELSE 0 END) +
MAX(CASE WHEN DetailStatusID = 2 THEN 1 ELSE 0 END) +
MAX(CASE WHEN DetailStatusID = 0 THEN 1 ELSE 0 END) >= 2
THEN 'MultiStatus'
WHEN COUNT(CASE WHEN DetailStatusID = 1 THEN 1 END) > 0 THEN 'Pending'
WHEN COUNT(CASE WHEN DetailStatusID = 2 THEN 1 END) > 0 THEN 'Open'
WHEN MAX(DetailStatusID) = 0 THEN 'Closed'
END AS Status
FROM Order AS o
JOIN OrderDetails AS od ON o.orderID = od.OrderIdOfDetail
GROUP BY orderID
The query uses a CASE expression in order to calculate the Status field:
It first checks whether the order contains at least one record with DetailStatusID = 1. If it does 'Pending' is returned.
It then checks whether the order contains at least one record with DetailStatusID = 2. If it does 'Open' is returned.
It then checks whether all records of the group have DetailStatusID = 0. In this case 'Closed' is returned. I assume that DetailStatusID cannot take negative vaues.
This uses a left join to an derived table (subquery) that uses conditional aggregation to count the different DetailStatusId. This will also return a result of Other for Status if, for some reason, the OrderId has no corresponding details record.
select o.*
, [Status]=case
when od.Status0 > 0 and od.Status0 = od.DetailCount
then 'Closed'
when od.Status1 > 0
then 'Pending'
when od.Status2 > 0
then 'Open'
else 'Other'
end
from orders as o
left join (
select
OrderIdOfDetail
, DetailCount = count(*)
, Status0 = sum(case when DetailStatusId = 0 then 1 else 0 end)
, Status1 = sum(case when DetailStatusId = 1 then 1 else 0 end)
, Status2 = sum(case when DetailStatusId = 2 then 1 else 0 end)
from OrderDetails
group by OrderIdOfDetail
) as od
on o.OrderId = od.OrderIdOfDetail
rextester demo: http://rextester.com/YPA79670
query results:
+---------+---------------------+-------------+---------+
| orderID | orderDate | OrderUserId | Status |
+---------+---------------------+-------------+---------+
| 10 | 01.01.2010 00:00:00 | 1 | Pending |
| 20 | 05.05.2011 00:00:00 | 2 | Pending |
+---------+---------------------+-------------+---------+
subquery only results:
+-----------------+-------------+---------+---------+---------+
| OrderIdOfDetail | DetailCount | Status0 | Status1 | Status2 |
+-----------------+-------------+---------+---------+---------+
| 10 | 5 | 1 | 2 | 2 |
| 20 | 3 | 1 | 1 | 1 |
+-----------------+-------------+---------+---------+---------+
select *, case when DetailStatusID = '1' then 'Pending'
when DetailStatusID = '2' then 'Open'
when DetailStatusID = '0' then 'Closed'
end as OrderStatusDescription
From OrderDetails
Where OrderIdOfDetail = '10'

SQL separate the count of one column

I have a SQL table that contains three columns:
userId
userName
item
and I created this SQL query which will count all the items types of one user:
select
count(ItemID) as 'count of all items types',
userId,
userName
from
userTable
where
ItemID in (2, 3, 4)
and userId = 1
group by
userId, userName
The result will be like this:
+--------+----------+--------------------------+
| userId | userName | count of all items types |
+--------+----------+--------------------------+
| 1 | kim | 25 |
and I am looking for a way to separate the counting of itemes types, so the result should be like this:
+--------+----------+----------------+----------------+-----------------+
| userId | userName | count of item1 | count of item2 | count of item3 |
+--------+----------+----------------+----------------+-----------------+
| 1 | kim | 10 | 10 | 5 |
SELECT
userID,
userName,
SUM(CASE WHEN ItemID = 2 THEN 1 ELSE 0 END) AS count_of_item1,
SUM(CASE WHEN ItemID = 3 THEN 1 ELSE 0 END) AS count_of_item2,
SUM(CASE WHEN ItemID = 4 THEN 1 ELSE 0 END) AS count_of_item3
FROM
My_Table
GROUP BY
userID,
userName
This is called conditional aggregation. Use CASE for this.
With COUNT:
select
count(case when ItemID = 1 then 1 end) as count_item1,
count(case when ItemID = 2 then 1 end) as count_item2,
count(case when ItemID = 3 then 1 end) as count_item3
...
(then 1 could also be anything else except null, e.g. then 'count me'. This works because COUNT counts non-null values and when omitting the ELSE in CASE WHEN you get null. You could also explicitly add else null.)
Or with SUM:
select
sum(case when ItemID = 1 then 1 else 0 end) as count_item1,
sum(case when ItemID = 2 then 1 else 0 end) as count_item2,
sum(case when ItemID = 3 then 1 else 0 end) as count_item3
...
This is how you would do it :
select userId,
username,
SUM(CASE WHEN ItemID = '2' THEN 1 ELSE 0 END) AS Item2-Cnt,
SUM(CASE WHEN ItemID = '3' THEN 1 ELSE 0 END) AS Item3-Cnt,
SUM(CASE WHEN ItemID = '4' THEN 1 ELSE 0 END) AS Item4-Cnt
FROM userTable
GROUP BY userID, userName