Find count in SQL using different set of dates - sql

I have below query to find the counts based on different date fields.
How can I get the result like below expected result?
The query shown is not returning the actual counts like the sample result.
+-------+-----------+------------+-----------+----------+
| WO_id | DateOpen | DateFinish | DateClose | Location |
+-------+-----------+------------+-----------+----------+
| 100 | 16-Dec-18 | 18-Dec-18 | 19-Dec-18 | A |
| 101 | 16-Dec-18 | 18-Dec-18 | 19-Dec-18 | A |
| 102 | 17-Dec-18 | 19-Dec-18 | 20-Dec-18 | C |
| 103 | 10-Dec-18 | 11-Dec-18 | 16-Dec-18 | D |
| 104 | 17-Dec-18 | 19-Dec-18 | 18-Dec-18 | E |
+-------+-----------+------------+-----------+----------+
Query (selection criteria : 16-Dec-2018 data only):
SELECT
COUNT(DateOpen) AS Opened,
COUNT(DateClose) AS closed,
COUNT(DateFinish) AS finished,
Location
FROM
JOB
WHERE
JOB.DateOpen BETWEEN '12/16/2018' AND DATEADD(DAY, 1, '12/16/2018')
OR JOB.DateClose BETWEEN '12/16/2018' AND DATEADD(DAY, 1, '12/16/2018')
OR JOB.DateFinish BETWEEN '12/16/2018' AND DATEADD(DAY, 1, '12/16/2018')
GROUP BY
Location
Expected result:
+--------+----------+--------+----------+
| opened | finished | closed | Location |
+--------+----------+--------+----------+
| 2 | 0 | 0 | A |
| 0 | 0 | 1 | D |
+--------+----------+--------+----------+

There is a trick you can with SUM and CASE, you use case to select 1 when it matches the criteria or 0 otherwise and then sum to "count" those items -- (since summing 0 or null is like not counting something). Here is the codez:
SELECT
SUM(CASE WHEN JOB.DateOpen BETWEEN '12/16/2018' AND DATEADD(DAY, 1, '12/16/2018') THEN 1 ELSE 0 END) AS opened,
SUM(CASE WHEN JOB.DateFinish BETWEEN '12/16/2018' AND DATEADD(DAY, 1, '12/16/2018') THEN 1 ELSE 0 END) AS finished,
SUM(CASE WHEN JOB.DateClose BETWEEN '12/16/2018' AND DATEADD(DAY, 1, '12/16/2018') THEN 1 ELSE 0 END) AS closed,
location
FROM JOB
group by Location

This should get you the results you're looking for. It only returns the rows that match the specific date criteria. The way you have your query currently built will result in it also pulling in rows that match 12/17/2018. You can test it here: https://rextester.com/MHT79618
DECLARE #SelectionDate DATETIME = '12/16/2018'
SELECT
SUM (CASE WHEN DateOpen = #SelectionDate THEN 1 ELSE 0 end) as Opened
,SUM (CASE WHEN DateClose = #SelectionDate THEN 1 ELSE 0 end)as closed
,SUM (CASE WHEN DateFinish = #SelectionDate THEN 1 ELSE 0 end)as finished
,Location
FROM JOB
WHERE JOB.DateOpen = #SelectionDate
or JOB.DateClose = #SelectionDate
or JOB.DateFinish = #SelectionDate
group by Location
edit damn, just saw Hogan answered while I was typing with basically the same answer.

In your original script, you count all rows and I think you might have the incorrect conditions. Please try following script.
create table JOB
(WO_id int,
DateOpen date,
DateFinish date,
DateClose date,
Location varchar(20))
insert into JOB values
(100,'16-Dec-18','18-Dec-18','19-Dec-18','A'),
(101,'16-Dec-18','18-Dec-18','19-Dec-18','A'),
(102,'17-Dec-18','19-Dec-18','20-Dec-18','C'),
(103,'10-Dec-18','11-Dec-18','16-Dec-18','D'),
(104,'17-Dec-18','19-Dec-18','18-Dec-18','E')
;with cte as (
SELECT
CASE WHEN DateOpen = '12/16/2018' THEN 1 ELSE 0 end as Opened,
CASE WHEN DateClose = '12/16/2018' THEN 1 ELSE 0 end as closed,
CASE WHEN DateFinish = '12/16/2018' THEN 1 ELSE 0 end as finished,
Location
FROM JOB )
select sum(Opened) as Opened,sum(closed) as closed,sum(finished) as finished,Location
from cte
WHERE Opened <>0 or closed<>0 or finished <>0
group by Location
/*
Opened closed finished Location
----------- ----------- ----------- --------------------
2 0 0 A
0 1 0 D
*/
Best Regards,
Rachel

Related

How to perform group by in SQL Server for specific output

I have a table with few records, I want to get month wise data along with count on one of the column. The output should contain Month and count of Isregistered flag.
Table structure
| Inserted On | IsRegistered |
+-------------+--------------+
| 10-01-2020 | 1 |
| 15-01-2020 | 1 |
| 17-01-2020 | null |
| 17-02-2020 | 1 |
| 21-02-2020 | null |
| 04-04-2020 | null |
| 18-04-2020 | null |
| 19-04-2020 | 1 |
Excepted output
| Inserted On | Registered | Not Registered
+-------------+------------+---------------
| Jan | 2 | 1
| Feb | 1 | 1
| Apr | 1 | 2
I tried by performing normal group by but didn't got desired output
SELECT
DATENAME(MONTH, dateinserted) AS [MonthName], COUNT(ISRegistered)
FROM
tablename
GROUP BY
(DATENAME(MONTH, dateinserted))
Note: here null is treated as not registered
You can use aggregation. I would include the year and use the month number rather than name, so:
select year(inserted_on), month(inserted_on),
coalesce(sum(is_registered), 0) as num_registered,
sum(case when is_registered is null then 1 else 0 end) as num_not_registered
from tablename
group by year(inserted_on), month(inserted_on)
order by year(inserted_on), month(inserted_on);
Note: If you really want the monthname and want to combine data from different years (which seems unlikely, but . . . ), then you can use:
select datename(month, inserted_on),
coalesce(sum(is_registered), 0) as num_registered,
sum(case when is_registered is null then 1 else 0 end) as num_not_registered
from tablename
group by datename(month, inserted_on)
order by month(min(inserted_on));
The GROUP BY should include both the year and month (so there's no overlapping) as well as the DATENAME (for display). Something like this
drop table if exists #tablename;
go
create table #tablename(dateinserted date, ISRegistered int);
insert #tablename values
('2020-12-01', 0),
('2020-11-02', 1),
('2020-11-03', 1),
('2020-12-01', 1),
('2020-12-03', 1),
('2020-11-02', 0);
select year(dateinserted) yr,
datename(month, dateinserted) AS [MonthName],
sum(ISRegistered) Registered ,
sum(1-ISRegistered) [Not Registered]
from #tablename
group by year(dateinserted), month(dateinserted), datename(month, dateinserted)
order by year(dateinserted), month(dateinserted);
yr MonthName Registered Not Registered
2020 November 2 1
2020 December 2 1

Sum (or count) multiple case statement

I have the following table:
Month | Item | Events | Party | Spirit | Faith |
May | 123 | 1 | 1 | 0 | 0 |
June |123 | 1 | 0 | 1 | 1 |
it is basically 1 for yes 0 for no. I need to know how many different categories each item is in each month
I need the following results:
Month | Item | Counts |
May | 123 | 2 |
June| 123 | 3 |
This is NOT working:
select Month, Item,
sum(case when EVENTS = 1 then 1 when PARTY = 1 then 1 when SPIRIT = 1 then 1 when FAITH = 1 then 1 else 0 end) as Counts
from TABLE
group by 1,2
Please help, thanks!
You don't need aggregation:
select Month, Item,
(events + party + spirit + faith) as counts
from t;
CREATE TABLE #T
(
Month varchar(10), Item int, Events bit, Party bit, Spirit bit , Faith bit
)
insert into #T
SELECT 'May' , 123 , 1 , 1 , 0 , 0 union
SELECT 'June' ,123 , 1 , 0 , 1 , 1
select Month, Item, CAST(Events AS INT) + CAST(Party AS INT)+ CAST(Spirit AS
INT) +CAST(Faith AS INT) from #T
Aggregation is not needed. Since the events, party, spirit and faith are bit columns, we need to cast it to int and then add it.

Get Month columns from datetime column and count entries

I have the following table:
| ID | Name | DateA | TimeToWork | TimeWorked |
|:--:|:----:|:----------:|:----------:|:----------:|
| 1 |Frank | 2013-01-01 | 8 | 5 |
| 2 |Frank | 2013-01-02 | 8 | NULL |
| 3 |Frank | 2013-01-03 | 8 | 7 |
| 4 |Jules | 2013-01-01 | 4 | 9 |
| 5 |Jules | 2013-01-02 | 4 | NULL |
| 6 |Jules | 2013-01-03 | 4 | 3 |
The table is very long, every person has an entry for every day in a year. For each person I have the Date he worked (DateA), the hours he has to work according to contract (TimeToWork) and the hours he worked (TimeWorked). As you can see some days a person didnt work on a day he had to. This is when a person took a full day overtime.
What I try to accomplish is to get the following table out of the first one above.
| Name | January | Feburary | March | ... | Sum |
|:----:|:----------:|:--------:|:-----:|:---:|:---:|
|Frank | 2 | 0 | 1 | ... | 12 |
|Jules | 5 | 1 | 3 | ... | 10 |
For each month I want to count all days where a person took A FULL day off and sum all up in the Sum column.
I tried something like Select (case when Datetime(month, DateA = 1 then count(case when timetowork - (case when timeworked then 0 end) = timetowork then 1 else 0 end) end) as 'January' but my TSQL is just not that good and the code doent work at all. Btw using this my select command would be about 40 lines.
I really would appreciate if anyone could help me or give me a link to a good source so I can read myself into it.
If I understand the question right, than Gordon Linoff's answer is a good beginning, but doesn't deal with "full day off".
select Name,
sum(case when month(DateA) = 01 and TimeWorked is null then 1 else 0 end) as Jan,
sum(case when month(DateA) = 02 and TimeWorked is null then 1 else 0 end) as Feb,
...
sum(case when month(DeteA) = 12 and TimeWorked is null then 1 else 0 end) as Dec,
sum(case when TimeWorked is null then 1 else 0 end) as Sum
from table T
where year(DateA) = 2013
group by name
This method solves the problem?
The correct syntax is conditional aggregation:
select name,
sum(case when month(datea) = 1 then timeworked else 0 end) as Jan,
sum(case when month(datea) = 2 then timeworked else 0 end) as Feb,
. . .
sum(case when month(datea) = 12 then timeworked else 0 end) as Dec,
sum(timeworked)
from table t
where year(datea) = 2013
group by name;
The CASE can be removed using bit logic
SELECT name
, January = SUM((1 - CAST(MONTH(DateA) - 1 as bit))
* (1 - CAST(COALESCE(TimeWorked, 0) as bit)))
, February = SUM((1 - CAST(MONTH(DateA) - 2 as bit))
* (1 - CAST(COALESCE(TimeWorked, 0) as bit)))
...
, December = SUM((1 - CAST(MONTH(DateA) - 12 as bit))
* (1 - CAST(COALESCE(TimeWorked, 0) as bit)))
, Total = SUM((1 - CAST(COALESCE(TimeWorked, 0) as bit)))
FROM table1
GROUP BY name;
To check if there is a dayoff the formula is:
(1 - CAST(COALESCE(TimeWorked, 0) as bit))
that is equivalent to TimeWorked IS NULL: the CAST to BIT return 1 for every value different from 0, 1 - BIT invert those values.
The month filter is:
(1 - CAST(MONTH(DateA) - %month% as bit))
using the same idea as before this formula return 1 only for the given month (the cast give 1 for every other month, the 1 - BIT invert that result)
Multipling the two formulas we have the days off only for the given month
You can get your required result by using pivot also. You can get more information about pivot here http://technet.microsoft.com/en-in/library/ms177410(v=sql.105).aspx
Also you can get your output using the following query. I did it for up to April only. You can extend it up to December.
Select [Name], [January], [February], [March], [April]
From
(
Select Name, MName, DaysOff from
(
select Name, DATENAME(MM, dateA) MName,
count(case isnull(timeworked,0) when 0 then 1 else null end) DaysOff
from tblPivot
Where Year(DateA) = 2013
group by Name, DATENAME(MM, dateA)
) A ) As B
pivot(Count(DaysOff)
For MName in ([January], [February],[March],[April])
) As Pivottable;

filter by Sum without Grouping

i have a resultset that i generate from a query that Looks like this:
Select Employee, Month, (select case when Status = '---' then 0 Else 1 end) as PlaningValue
From PlanningTable PT
Where Month >= #From Month and Month <= #ToMonth
The Result of this Looks something like this:
|Employee| Month | PlaningValue |
|George | 2014-01 | 1 |
|George | 2014-02 | 1 |
|George | 2014-03 | 0 |
|Andrew | 2014-01 | 0 |
|Andrew | 2014-02 | 1 |
|Andrew | 2014-03 | 0 |
|Howard | 2014-01 | 1 |
|Howard | 2014-02 | 1 |
|Howard | 2014-03 | 1 |
Now what i want is the following:
Filter out Employee's who, over the three month period, have a total planing Value of 3,
in the example above, Howard would be filtered out.
Is there a way to do this nicely or is it all just impossible to even thin ?
(Remark: Since i am going to use the Query on Reporting Services, i can't use the OVER function)
Thank you all for your help
This looks to be SQL Server syntax, as such I you can use windowed functions:
WITH CTE AS
( SELECT Employee,
Month,
PlanningValue = CASE WHEN Status = '---' THEN 0 ELSE 1 END,
Total = SUM(CASE WHEN Status = '---' THEN 0 ELSE 1 END)
OVER (PARTITION BY Employee)
FROM PlanningTable
WHERE Month >= #FromDate
AND Month <= #ToMonth
)
SELECT Employee, Month, PlanningValue
FROM CTE
WHERE Total != 3;
Simplified Example on SQL Fiddle
Try:
select pt.employee, pt.month, pt.planningvalue
from planningtable pt
join planningtable pt2 on pt.employee = pt2.employee
join planningtable pt3 on pt.employee = pt3.employee
join planningtable pt4 on pt.employee = pt4.employee
where month >= #mofrom and month <= #tomonth
and pt2.month = #tomonth
and pt3.month in (select month from planningtable where month > #mofrom and month < #tomonth)
and pt4.month = #mofrom
and pt2.planningvalue + pt3.planningvalue + pt4.planningvalue <> 3

Select most the recent event date for a specific event type from a related table

I have two tables, Case and CaseEvents.
The events are basically a user updating a case and there are lots of different types of event.
I have a KPI that two of those events (which are different types) have to have happened within 3 minutes - the Response Time.
Response Time is calculated by subtracting the date in EventTypeRef 1 from the date in EventTypeRef 2 OR EventTypeRef 3.
I include my desired result in the table below.
Apologies if this is simple, I'm a right newbie and couldn't find anything online that was similar (though maybe I was using the wrong terminology in my searches). Thanks for your help.
I am using SQL Server 2005
'Case table'
----------
CaseRef | CaseNo
1 | 1001
2 | 1002
CaseEvents table
----------------
CaseRef | EventType | EventStartDate | EventTypeRef
1 | 'LTE Detected' | 01/11/2013 09:05:36 | 1
1 | 'Ambulance called' | 01/11/2013 09:06:27 | 2
2 | 'LTE Detected' | 02/11/2013 09:11:22 | 1
2 | 'Patient will call ambulance' | 02/11/2013 09:13:01 | 3
1 | 'Case updated' | 01/11/2013 09:09:34 | 4
1 | 'Case marked complete' | 01/11/2013 09:13:34 | 5
Desired result
--------------
CaseNo | LTEDetected | 'LTE Acted On' | 'Response time' |
1 | 01/11/2013 09:05:36 | 01/11/2013 09:06:27 | 00:00:51 |
2 | 02/11/2013 09:11:22 | 02/11/2013 09:13:01 | 00:01:39 |
You can put a CASE statement within an aggregate, so you are only checking the event types you are interested in.
SELECT c.CaseNo,
LTEDetected = MIN(CASE WHEN ce.EventTypeRef = 1 THEN ce.EventStartDate END),
LTEActedOn = MIN(CASE WHEN ce.EventTypeRef IN (2, 3) THEN ce.EventStartDate END),
Responsetime = DATEDIFF(SECOND,
MIN(CASE WHEN ce.EventTypeRef = 1 THEN ce.EventStartDate END),
MIN(CASE WHEN ce.EventTypeRef IN (2, 3) THEN ce.EventStartDate END)
)
FROM Case c
INNER JOIN CaseEvents ce
ON c.CaseRef = ce.CaseRef
GROUP BY c.CaseNo;
Example on SQL Fiddle
select CaseRef
, datediff(second, StartDt, EndDt) as ResponseTime
from (
select CaseRef
, max(case when EventTypeRef = 1 then EventStartDate end) as StartDt
, min(case when EventTypeRef in (2,3) then EventStartDate end) as EndDt
from CaseEvents
group by
CaseRef
) as SubQueryAlias