Group count and Pivot Query - sql

If Docs with A and B then count added to DocCompleted. If only A or B or Null then count added to unCompleted.
create table #TempRecords
(
EmpId int not null,
Doc_Name nvarchar(50),
DateCreated datetime ,
)
insert into #TempRecords values
(1001,'Doc_A','2016-10-15 07:57:37'),
(1001,'Doc_B','2016-10-15 07:57:37'),
(1001,'Doc_A','2016-10-15 07:57:37'),
(1001,'Doc_A','2016-10-15 07:57:37'),
(2001,'Doc_A','2016-10-15 07:57:37'),
(2001,'Doc_B','2016-10-15 07:57:37'),
(2001,'Doc_A','2016-10-15 07:57:37'),
(2001,'Doc_A','2016-10-15 07:57:37'),
(3001,null,null),
(3001,'Doc_A','2016-10-15 14:57:37'),
(3004,null,null)
select * from #TempRecords

You can do this with two levels of aggregation:
select count(*) as EmpCount,
sum(case when num_a > 0 and num_b > 0 then 1 else 0 end) as DocCompletedCount,
sum(case when num_a = 0 or num_b = 0 then 1 else 0 end) as DocUnCompletedCount
from (select empid,
sum(case when doc_name = 'Doc_A' then 1 else 0 end) as num_a,
sum(case when doc_name = 'Doc_B' then 1 else 0 end) as num_b
from #temprecords
group by empid
) t;
Or, if you want to be fancy (concise?):
select count(*) as EmpCount,
sum(has_a * has_b),
sum(1 - has_a * has_b) as DocUnCompletedCount
from (select empid,
max(case when doc_name = 'Doc_A' then 1 else 0 end) as has_a,
max(case when doc_name = 'Doc_B' then 1 else 0 end) as has_b
from #temprecords
group by empid
) t;

Count(Distinct ...) and a conditional aggregation may do the trick
Select EmpCount = count(Distinct EmpID)
,DocCompletedCount = count(Distinct Doc_Name)
,unCompletedCount = sum(case when Doc_Name is null then 1 else 0 end)
From #TempRecords
Returns
EmpCount DocCompletedCount unCompletedCount
4 2 2

Sorry, but what does the # in front of the table name do? It seems like it does nothing at all. When I removed it, all solutions worked for me. Just curious.

Related

select from table where a=1 and a=2

i have a four requirement (may be four select is ok) where I need to find from single table, if customer has
a. apple and samsung
b. no_apple and no_samsung
c. apple and no_samsung
d. no_apple and samsung
my table be like...
cust_name device
john apple
john samsung
dave apple
tim samsung
patrick nokia
rick nokia
so expect output be like...
a:- output ( both apple and samsung)
count(*)
1
b:-output (no_apple and no_samsung)
count(*)
2
c:-output (apple and no_samsung)
count(*)
1
d:-output (no_apple and samsung)
count(*)
1
You can do it all in a single query using conditional aggregation:
SELECT COUNT(CASE WHEN num_apple > 0 AND num_samsung > 0 THEN 1 END)
AS apple_and_samsung,
COUNT(CASE WHEN num_apple = 0 AND num_samsung > 0 THEN 1 END)
AS no_apple_and_samsung,
COUNT(CASE WHEN num_apple > 0 AND num_samsung = 0 THEN 1 END)
AS apple_and_no_samsung,
COUNT(CASE WHEN num_apple = 0 AND num_samsung = 0 THEN 1 END)
AS no_apple_and_no_samsung
FROM (
SELECT cust_name,
COUNT(CASE device WHEN 'apple' THEN 1 END) AS num_apple,
COUNT(CASE device WHEN 'samsung' THEN 1 END) AS num_samsung
FROM table_name
GROUP BY cust_name
)
Which, for the sample data:
CREATE TABLE table_name (cust_name, device) AS
SELECT 'john', 'apple' FROM DUAL UNION ALL
SELECT 'john', 'samsung' FROM DUAL UNION ALL
SELECT 'dave', 'apple' FROM DUAL UNION ALL
SELECT 'tim', 'samsung' FROM DUAL UNION ALL
SELECT 'patrick', 'nokia' FROM DUAL UNION ALL
SELECT 'rick', 'nokia' FROM DUAL;
Outputs:
APPLE_AND_SAMSUNG
NO_APPLE_AND_SAMSUNG
APPLE_AND_NO_SAMSUNG
NO_APPLE_AND_NO_SAMSUNG
1
1
1
2
You can also do it by PIVOTing twice:
SELECT *
FROM table_name
PIVOT (
COUNT(DISTINCT device) FOR device IN (
'apple' AS apple,
'samsung' AS samsung
)
)
PIVOT (
COUNT(cust_name) FOR (apple, samsung) IN (
(1, 1) AS apple_and_samsung,
(1, 0) AS apple_and_no_samsung,
(0, 1) AS no_apple_and_samsung,
(0, 0) AS no_apple_and_no_samsung
)
)
db<>fiddle here
You might add proper HAVING clauses for each case after GROUPing by cust_name column such as
a)
SELECT COUNT(DISTINCT cust_name)
FROM t
GROUP BY cust_name
HAVING SUM(CASE WHEN device ='apple' THEN 1 ELSE 0 END)
* SUM(CASE WHEN device ='samsung' THEN 1 ELSE 0 END) = 1;
b)
SELECT SUM(COUNT(DISTINCT cust_name))
FROM t
GROUP BY cust_name
HAVING MIN(CASE WHEN device ='apple' THEN 0 ELSE 1 END)
* MIN(CASE WHEN device ='samsung' THEN 0 ELSE 1 END) = 1;
c)
SELECT COUNT(DISTINCT cust_name)
FROM t
GROUP BY cust_name
HAVING MIN(CASE WHEN device ='samsung' THEN 0 ELSE 1 END)
* MIN(CASE WHEN device ='apple' THEN 1 ELSE 0 END) = 1;
d)
SELECT COUNT(DISTINCT cust_name)
FROM t
GROUP BY cust_name
HAVING MIN(CASE WHEN device ='apple' THEN 0 ELSE 1 END)
* MIN(CASE WHEN device ='samsung' THEN 1 ELSE 0 END) = 1
Demo

SQL - % Breakout by Hour

I'm trying to write a query that will break out what percentage of time is utilized within a hour given a time range.
Sample data:
declare #date table(id int, Start_time time, End_time time)
Insert Into #date(id, Start_time, End_time) values
(0, '09:15', '17:15')
, (1, '10:45', '16:30')
, (2, '08:05', '17:45')
, (3, '07:00', '15:00')
, (4, '07:30', '8:30')
Looking to get output like this:
Any help would greatly be appreciated.
By using an ad-hoc tally table to create every minute of the day. This assumes you are processing to the minute. Then it becomes a small matter of the conditional aggregation.
Example
;with cte as (
Select A.ID
,Hrs = datepart(hour,T)
,Cnt = convert(decimal(10,2),sum(1.0) / 60)
From #date A
Join (
Select Top (1440) T=dateadd(MINUTE,-1+Row_Number() Over (Order By (Select NULL)),0) From master..spt_values n1,master..spt_values n2
) B on T >=convert(datetime,Start_Time) and t<convert(datetime,End_Time)
Group By A.ID,datepart(hour,T)
)
Select ID
,h3 = sum(case when Hrs=3 then Cnt else 0 end)
,h4 = sum(case when Hrs=4 then Cnt else 0 end)
,h5 = sum(case when Hrs=5 then Cnt else 0 end)
,h6 = sum(case when Hrs=6 then Cnt else 0 end)
,h7 = sum(case when Hrs=7 then Cnt else 0 end)
,h8 = sum(case when Hrs=8 then Cnt else 0 end)
,h9 = sum(case when Hrs=9 then Cnt else 0 end)
,h10 = sum(case when Hrs=10 then Cnt else 0 end)
,h11 = sum(case when Hrs=11 then Cnt else 0 end)
,h12 = sum(case when Hrs=12 then Cnt else 0 end)
,h13 = sum(case when Hrs=13 then Cnt else 0 end)
,h14 = sum(case when Hrs=14 then Cnt else 0 end)
,h15 = sum(case when Hrs=15 then Cnt else 0 end)
,h16 = sum(case when Hrs=16 then Cnt else 0 end)
,h17 = sum(case when Hrs=17 then Cnt else 0 end)
,h18 = sum(case when Hrs=18 then Cnt else 0 end)
From cte
Group By ID
Returns

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;

group by and select max with value null

I have a next problem with query
SELECT
T.DETALLE_BECA_ANIO anio,
T.DETALLE_BECA_MES mes,
T.DETALLE_BECA_NIVEL_EDU_ID edu_id,
T.DETALLE_BECA_TRAMO_ID tr_id,
MAX(
CASE
WHEN T.DETALLE_BECA_TIPO_BENE_ID IS NULL
THEN NVL(DETALLE_BECA_VALOR,0)
ELSE 0
END) mant ,
MAX(
CASE
WHEN T.DETALLE_BECA_TIPO_BENE_ID = 1
THEN NVL(DETALLE_BECA_VALOR,0)
ELSE 0
END) tras
FROM
(SELECT DETALLE_BECA_NIVEL_EDU_ID,
DETALLE_BECA_BECA_ID,
DETALLE_BECA_TIPO_BENE_ID,
DETALLE_BECA_VALOR,
DETALLE_BECA_MES,
DETALLE_BECA_REGION_ID,
DETALLE_BECA_PROVINCIA_ID,
DETALLE_BECA_ANIO,
DETALLE_BECA_TRAMO_ID,
DETALLE_BECA_COMUNA_ID
FROM TBL_DETALLE_BECAS
WHERE (DETALLE_BECA_TIPO_BENE_ID = 1
OR DETALLE_BECA_TIPO_BENE_ID IS NULL)
and DETALLE_BECA_BECA_ID = 1
and detalle_beca_mes = 3
) T
GROUP BY T.DETALLE_BECA_BECA_ID,
T.DETALLE_BECA_TRAMO_ID,
T.DETALLE_BECA_REGION_ID,
T.DETALLE_BECA_PROVINCIA_ID,
T.DETALLE_BECA_ANIO,
T.DETALLE_BECA_MES,
T.DETALLE_BECA_NIVEL_EDU_ID,
T.DETALLE_BECA_COMUNA_ID
ORDER BY T.DETALLE_BECA_BECA_ID,
T.DETALLE_BECA_MES,
T.DETALLE_BECA_NIVEL_EDU_ID
output:
"ANIO" "MES" "EDU_ID" "TR_ID" "MANT" "TRAS"
2017 3 2 0.62 0 NULL
2017 3 3 1.24 6 NULL
2017 3 NULL 1.0 NULL 1
I need that sum value where EDU_ID is null with value 2,3 in TR_ID and replace value null in "tras" with value from EDU is null
"ANIO" "MES" "EDU_ID" "TR_ID" "MANT" "TRAS"
2017 3 2 1.62 0 1
2017 3 3 2.24 6 1
I writed query with min(edu_id) or max(edu_id ) but could not solve my problem.
The other thing that occurred to me is to make a join with the same table
First, this makes more sense as your query:
SELECT T.DETALLE_BECA_ANIO as anio, T.DETALLE_BECA_MES as mes,
T.DETALLE_BECA_NIVEL_EDU_ID as edu_id, T.DETALLE_BECA_TRAMO_ID as tr_id,
MAX(CASE WHEN T.DETALLE_BECA_TIPO_BENE_ID IS NULL
THEN NVL(DETALLE_BECA_VALOR, 0)
ELSE 0
END) as mant ,
MAX(CASE WHEN T.DETALLE_BECA_TIPO_BENE_ID = 1
THEN NVL(DETALLE_BECA_VALOR,0)
ELSE 0
END) tras
FROM TBL_DETALLE_BECAS
WHERE (DETALLE_BECA_TIPO_BENE_ID = 1 OR DETALLE_BECA_TIPO_BENE_ID IS NULL) AND
DETALLE_BECA_BECA_ID = 1 AND
detalle_beca_mes = 3
GROUP BY T.DETALLE_BECA_ANIO, T.DETALLE_BECA_MES,
T.DETALLE_BECA_NIVEL_EDU_ID, T.DETALLE_BECA_TRAMO_ID
ORDER BY T.DETALLE_BECA_BECA_ID, T.DETALLE_BECA_MES, T.DETALLE_BECA_NIVEL_EDU_ID;
This eliminates the subquery (unnecessary) and only aggregates by the columns being returned. A proper query might fix your problem.
But, you seem to want to use NULL to be "all" for the other columns. If so, something like this will work:
WITH t as (
SELECT T.DETALLE_BECA_ANIO as anio, T.DETALLE_BECA_MES as mes,
T.DETALLE_BECA_NIVEL_EDU_ID as edu_id, T.DETALLE_BECA_TRAMO_ID as tr_id,
MAX(CASE WHEN T.DETALLE_BECA_TIPO_BENE_ID IS NULL
THEN NVL(DETALLE_BECA_VALOR, 0)
ELSE 0
END) as mant ,
MAX(CASE WHEN T.DETALLE_BECA_TIPO_BENE_ID = 1
THEN NVL(DETALLE_BECA_VALOR,0)
ELSE 0
END) tras
FROM TBL_DETALLE_BECAS
WHERE (DETALLE_BECA_TIPO_BENE_ID = 1 OR DETALLE_BECA_TIPO_BENE_ID IS NULL) AND
DETALLE_BECA_BECA_ID = 1 AND
detalle_beca_mes = 3
GROUP BY T.DETALLE_BECA_ANIO, T.DETALLE_BECA_MES,
T.DETALLE_BECA_NIVEL_EDU_ID, T.DETALLE_BECA_TRAMO_ID
)
SELECT t.ANIO, t.MES, t.EDU_ID,
COALESCE(t.TR_ID, 0) + COALESCE(tnull.TR_ID, 0) as TR_ID,
t.MANT,
COALESCE(t.TRAS, 0) + COALESCE(tnull.TRAS, 0) as TRAS
FROM t LEFT JOIN
(SELECT t.*
FROM t
WHERE t.edu_id IS NULL
) tnull
ON tnull.ANIO = t.ANIO AND tnull.MES = t.MES
WHERE t.edu_id IS NOT NULL
ORDER BY T.DETALLE_BECA_BECA_ID, T.DETALLE_BECA_MES, T.DETALLE_BECA_NIVEL_EDU_ID;

How to get summary totals on unique IDs using analytics?

My repeating table has duplicate ids but I want summary statistics on unique ids.
Detail_id code book tree
----------- ------ ------ ------
1 BR54 COOK PINE
1 BR55 COOK PINE
1 BR51 COOK PINE
2 BR55 COOK MAPL
2 BR60 COOK MAPL
3 BR61 FORD PINE
3 BR54 FORD PINE
3 BR55 FORD PINE
Here's my query which is also on SQLFiddle
select count(case detail_book when 'COOK' THEN 1 else 0 end) as cook_total,
count(case detail_book when 'FORD' THEN 1 else 0 end) as ford_total,
count(case detail_tree when 'PINE' THEN 1 else 0 end) as pine_total,
count(case detail_book when 'MAPL' THEN 1 else 0 end) as mapl_total
from detail_records;
Desired results:
COOK_TOTAL FORD_TOTAL PINE_TOTAL MAPL_TOTL
---------- ---------- ---------- ----------
2 1 2 1
You could use analytic functions and an inline view to avoid the duplicate counting issue:
select sum(case when detail_book = 'COOK' and book_cntr = 1 then 1 else 0 end) as cook_total,
sum(case when detail_book = 'FORD' and book_cntr = 1 then 1 else 0 end) as ford_total,
sum(case when detail_tree = 'PINE' and tree_cntr = 1 then 1 else 0 end) as pine_total,
sum(case when detail_tree = 'MAPL' and tree_cntr = 1 then 1 else 0 end) as mapl_total
from (select d.*,
row_number() over(partition by detail_book, detail_id order by detail_book, detail_id) as book_cntr,
row_number() over(partition by detail_tree, detail_id order by detail_tree, detail_id) as tree_cntr
from detail_records d) v
Fiddle: http://sqlfiddle.com/#!4/889a8/31/0
I don't think you need analytic functions here:
SELECT COUNT(DISTINCT CASE WHEN detail_book = 'COOK' THEN detail_id END) AS cook_total
, COUNT(DISTINCT CASE WHEN detail_book = 'FORD' THEN detail_id END) AS ford_total
, COUNT(DISTINCT CASE WHEN detail_tree = 'PINE' THEN detail_id END) AS pine_total
, COUNT(DISTINCT CASE WHEN detail_tree = 'MAPL' THEN detail_id END) AS mapl_total
FROM detail_records;
The CASE statement returns NULL when the values don't match; those aren't counted.
Updated SQL Fiddle here. By the way, in your query you were trying to match detail_book to MAPL when I think you wanted to match detail_tree, and my query reflects that.
This answer is based on your example http://sqlfiddle.com/#!4/889a8/29 which you can use to avoid the duplicate ids by getting the distinct ids for detail_book and detail_tree
Kindly check the result here http://sqlfiddle.com/#!4/889a8/44
select sum(case detail_book
when 'COOK' THEN 1
else 0
end) as cook_total,
sum(case detail_book
when 'FORD' THEN 1
else 0
end) as ford_total,
sum(case detail_tree
when 'PINE' THEN 1
else 0
end) as pine_total,
sum(case detail_tree
when 'MAPL' THEN 1
else 0
end) as mapl_total
from
(select distinct detail_id,detail_book,detail_tree
from
detail_records);
You can modify your query to get what you want just by removing the else clause:
select count(case detail_book when 'COOK' THEN 1 end) as cook_total,
count(case detail_book when 'FORD' THEN 1 end) as ford_total,
count(case detail_tree when 'PINE' THEN 1 end) as pine_total,
count(case detail_book when 'MAPL' THEN 1 end) as mapl_total
from detail_records;
The default for case without an else is NULL, so the count() works. Personally, I prefer sum() for this type of aggregation:
select sum(case when detail_book = 'COOK' THEN 1 else 0 end) as cook_total,
sum(case when detail_book = 'FORD' THEN 1 else 0 end) as ford_total,
sum(case when detail_tree = 'PINE' THEN 1 else 0 end) as pine_total,
sum(case when detail_book = 'MAPL' THEN 1 else 0 end) as mapl_total
from detail_records;
Apart from analytical functions, I'd probably use an approach by first "flattening the table" (union all) and then pivoting the result:
select *
from (
select detail_book i
from detail_records
group by detail_id, detail_book
union all
select detail_tree
from detail_records
group by detail_id, detail_tree
)
pivot(count(i) for i in ('COOK', 'FORD', 'PINE', 'MAPL'))
;
sql fiddle
select *
from (
select decode(detail_book,'FORD','FORD_TOTAL','COOK','COOK_TOTAL','MAPL','MAPL_TOTAL','PINE','PINE_TOTAL','OTHER') i,
count(distinct detail_id) j
from detail_records
group by detail_book
union all
select DECODE(detail_tree,'FORD','FORD_TOTAL','COOK','COOK_TOTAL','MAPL','MAPL_TOTAL','PINE','PINE_TOTAL','OTHER') i,
count(distinct detail_id) j
from detail_records
group by detail_tree
)
pivot(sum(j) for i in ('COOK_TOTAL', 'FORD_TOTAL', 'PINE_TOTAL', 'MAPL_TOTAL','OTHER'))
;