Get Month columns from datetime column and count entries - sql

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;

Related

Calculate data in Pivot

I have the following SQL table with 4 columns.
Table Name: tblTimeTransaction
Columns: EmployeeNumber, TransactionDate, CodeType, TimeShowninSeconds
CodeType has values : REG, OT1, OT2, OT3 respectively
I want it to show like this using pivot using 15 days incrementals starting from Jan 1 2020 onwards:
Employee Number | Effective Date | REG | OT1 | OT2 | OT3
E12345 | Between 10-1 till 10-15 | 200 | 100 | 50 | 45
E15000 | Between 10-1 till 10-15 | 400 | 600 | 903 | 49
E12345 | Between 10-15 till 10-31 | 200 | 100 | 50 | 45
E15000 | Between 10-15 till 10-31 | 400 | 600 | 903 | 49
E12346 | Between 11-1 till 11-15 | 4200 | 100 | 50 | 45
E15660 | Between 11-1 till 11-15 | 1200 | 600 | 6903 | 49
My SQL Code so far:
SELECT
Employee Number,
[TransactionDate] as [Effective Date],
[REG],
[OT1],
[OT2],
[OT3]
FROM
( SELECT Employee Number, TransactionDate, CodeType, TimeInSeconds
FROM [tblTimetransaction]
) ps
PIVOT
( SUM (TimeInSeconds)
FOR CodeType IN ( [REG], [OT1], [OT2], [OT3])
) AS pvt
where TransactionDate between '2020-01-01' and '2020-12-31'
If I follow you correctly, you can truncate the effective_date to either the 1st of 15th of the month depending on their day of the month, then use conditional aggregation to compute the total time_in_seconds for each code_type:
select employee_number,
datefromparts(year(effective_date), month(effective_date), case when day(effective_date) < 15 then 1 else 15 end) as dt,
sum(case when code_type = 'REG' then time_in_seconds else 0 end) as reg,
sum(case when code_type = 'OT1' then time_in_seconds else 0 end) as ot1,
sum(case when code_type = 'OT2' then time_in_seconds else 0 end) as ot2,
sum(case when code_type = 'OT3' then time_in_seconds else 0 end) as ot3
from tblTimetransaction
where effective_date >= '20200101' and effective_date < '20210101'
group by employee_number,
datefromparts(year(effective_date), month(effective_date), case when day(effective_date) < 15 then 1 else 15 end)

Find count in SQL using different set of dates

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

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.

Count who paid group by 1, 2 or 3+

I have a payment table like the example below and I need a query that gives me how many IDs paid (AMOUNT > 0) 1 time, 2 times, 3 or more times. Example:
+----+--------+
| ID | AMOUNT |
+----+--------+
| 1 | 50 |
| 1 | 0 |
| 2 | 10 |
| 2 | 20 |
| 2 | 15 |
| 2 | 10 |
| 3 | 80 |
+----+--------+
I expect the result:
+-----------+------------+-------------+
| 1 payment | 2 payments | 3+ payments |
+-----------+------------+-------------+
| 2 | 0 | 1 |
+-----------+------------+-------------+
ID 1: Paid 1 time (50). The other payment is 0, so I did not count. So, 1 person paid 1 time.
ID 2: Paid 3 times (10,20,15). So, 1 person paid 3 or more time.
ID 3: Paid 1 time (80). So, 2 persons paid 1 time.
I'm doing manually on excel right now but I'm pretty sure there is a more practical solution. Any ideas?
A little sub-query will do the trick
Declare #YOurTable table (ID int, AMOUNT int)
Insert into #YourTable values
( 1 , 50 ),
( 1 , 0) ,
( 2 , 10) ,
( 2 , 20) ,
( 2 , 15) ,
( 2 , 10) ,
( 3 , 80)
Select [1_Payment] = sum(case when Cnt=1 then 1 else 0 end)
,[2_Payment] = sum(case when Cnt=2 then 1 else 0 end)
,[3_Payment] = sum(case when Cnt>2 then 1 else 0 end)
From (
Select id
,Cnt=count(*)
From #YourTable
Where Amount<>0
Group By ID
) A
Returns
1_Payment 2_Payment 3_Payment
2 0 1
To get the output you want try using a table to form the data and then SELECT from that:
with c as (
select count(*) count from mytable where amount > 0 group by id)
select
sum(case count when 1 then 1 else 0 end) "1 Payment"
, sum(case count when 2 then 1 else 0 end) "2 Payments"
, sum(case when count > 2 then 1 else 0 end) "3 Payments"
from c
Here is an example you can play with to see how the query is working.

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