SQL Sum over several columns of an entry with ad hoc created columns - sql

I have the following table, like:
woker
date
amount
jeff
04-04-2022
4.00
jeff
04-05-2022
2.00
jeff
04-08-2022
3.50
dave
04-04-2022
1.00
dave
04-07-2022
6.50
It contains the date and the amount of hours worked by a worker.
Now I want to create a table like the following with a select to show hours per weekday. The column "count" should represent the amount of days where the worker has hours. The column "sum" should sum the hours for this week.
So the final result should be like this:
worker
mon
tue
wed
thu
fri
sat
sun
count
sum
jeff
4.00
2.00
null
null
3.50
null
null
3
9.50
dave
1.00
null
null
6.50
null
null
null
2
7.50
My statement so far is:
SELECT worker,
CASE WHEN DATEPART(weekday,date) = 1 THEN amount END AS mon,
CASE WHEN DATEPART(weekday,date) = 2 THEN amount END AS tue,
CASE WHEN DATEPART(weekday,date) = 3 THEN amount END AS wed,
CASE WHEN DATEPART(weekday,date) = 4 THEN amount END AS thu,
CASE WHEN DATEPART(weekday,date) = 5 THEN amount END AS fri,
CASE WHEN DATEPART(weekday,date) = 6 THEN amount END AS sat,
CASE WHEN DATEPART(weekday,date) = 7 THEN amount END AS sun
FROM table
So now I need help to get the last two columns. Can anybody explain, how to sum up / count values over multiple columns withi one entry?
Thank you.

We use the function SUM so as to be able to agregate the different lines. We add the week number in the SELECT and the GROUP BY so as to separate the weeks and know which week of the year we are looking at. We could also add the year if the same table will be used for long enough.
SET DATEFIRST 1;
SELECT
DATEPART(week, date) AS "week",
worker,
CASE WHEN DATEPART(weekday,date) = 1 THEN amount END ) AS mon,
SUM( CASE WHEN DATEPART(weekday,date) = 2 THEN amount END ) AS tue,
SUM( CASE WHEN DATEPART(weekday,date) = 3 THEN amount END ) AS wed,
SUM( CASE WHEN DATEPART(weekday,date) = 4 THEN amount END ) AS thu,
SUM( CASE WHEN DATEPART(weekday,date) = 5 THEN amount END ) AS fri,
SUM( CASE WHEN DATEPART(weekday,date) = 6 THEN amount END ) AS sat,
SUM( CASE WHEN DATEPART(weekday,date) = 7 THEN amount END ) AS sun,
COUNT(DISTINCT DATEPART(weekday,date) ) AS "count",
amount AS "sum"
FROM table
GROUP BY
DATEPART(week, date),
worker
ORDER BY
DATEPART(week, date),
worker;

Conditional aggregation is an option (don't forget to set the first day of the week using SET DATEFIRST):
SET DATEFIRST 1
SELECT
worker,
SUM(CASE WHEN DATEPART(weekday, date) = 1 THEN amount END) AS mon,
SUM(CASE WHEN DATEPART(weekday, date) = 2 THEN amount END) AS tue,
SUM(CASE WHEN DATEPART(weekday, date) = 3 THEN amount END) AS wed,
SUM(CASE WHEN DATEPART(weekday, date) = 4 THEN amount END) AS thu,
SUM(CASE WHEN DATEPART(weekday, date) = 5 THEN amount END) AS fri,
SUM(CASE WHEN DATEPART(weekday, date) = 6 THEN amount END) AS sat,
SUM(CASE WHEN DATEPART(weekday, date) = 7 THEN amount END) AS sun,
COUNT(DISTINCT DATEPART(weekday, date)) AS [count],
SUM(amount) AS [sum]
FROM (VALUES
('jeff', CONVERT(date, '20220404'), 4.00),
('jeff', CONVERT(date, '20220405'), 2.00),
('jeff', CONVERT(date, '20220408'), 3.50),
('dave', CONVERT(date, '20220404'), 1.00),
('dave', CONVERT(date, '20220407'), 6.50)
) t (worker, date, amount)
GROUP BY worker
ORDER BY worker
Result:
worker
mon
tue
wed
thu
fri
sat
sun
count
sum
dave
1.00
6.50
2
7.50
jeff
4.00
2.00
3.50
3
9.50
If you want to summarize the input data based on time interval, you need a different statement:
SET DATEFIRST 1
SELECT
worker,
--DATEPART(week, date) AS week,
DATEPART(month, date) AS month,
SUM(CASE WHEN DATEPART(weekday, date) = 1 THEN amount END) AS mon,
SUM(CASE WHEN DATEPART(weekday, date) = 2 THEN amount END) AS tue,
SUM(CASE WHEN DATEPART(weekday, date) = 3 THEN amount END) AS wed,
SUM(CASE WHEN DATEPART(weekday, date) = 4 THEN amount END) AS thu,
SUM(CASE WHEN DATEPART(weekday, date) = 5 THEN amount END) AS fri,
SUM(CASE WHEN DATEPART(weekday, date) = 6 THEN amount END) AS sat,
SUM(CASE WHEN DATEPART(weekday, date) = 7 THEN amount END) AS sun,
COUNT(DISTINCT DATEPART(weekday, date)) AS [count],
SUM(amount) AS [sum]
FROM (VALUES
('jeff', CONVERT(date, '20220404'), 4.00),
('jeff', CONVERT(date, '20220405'), 2.00),
('jeff', CONVERT(date, '20220408'), 3.50),
('dave', CONVERT(date, '20220404'), 1.00),
('dave', CONVERT(date, '20220407'), 6.50)
) t (worker, date, amount)
--GROUP BY worker, DATEPART(week, date)
--ORDER BY worker, DATEPART(week, date)
GROUP BY worker, DATEPART(month, date)
ORDER BY worker, DATEPART(month, date)

Instead om doing alot aggregates, cases and dateparts I would do a pivot table out of it like below. (Note that my days-weekdays ref may not be the same as yours)
Test data:
declare #tbl table (worker varchar(20), [date] date, amount dec(5,2))
INSERT INTO #tbl
SELECT 'jeff', '04-04-2022', 4.00
UNION SELECT 'jeff', '05-04-2022', 2.00
UNION SELECT 'jeff', '08-04-2022', 3.50
UNION SELECT 'dave', '04-04-2022', 1.00
UNION SELECT 'dave', '07-04-2022', 6.50
The actual code:
SELECT
worker
, [1] as sun
, [2] as mon
, [3] as tue
, [4] as wed
, [5] as thu
, [6] as fri
, [7] as sat
, [count]
, [sum]
FROM (select
worker
, amount
, datepart(weekday, t.[date]) dp
, COUNT(amount) OVER( partition by worker) as [count]
, SUM(amount) OVER( partition by worker) as [sum]
from #tbl t
) p
PIVOT(
SUM(amount)
FOR dp in ([1], [2], [3], [4], [5], [6], [7])
) piv

Related

Grouping several years' data by week

I am using Covid world data, which tracks number of new_cases every day. I have data from 2020-01-01 to present day. This is my current query:
SELECT MIN(date) as week, datepart(iso_week, date) week_num, sum(new_cases) as [Total Cases]
FROM covid_data_cleaned
GROUP BY DATEPART(iso_week, date), DATEPART(year, date)
ORDER BY MIN(date) DESC
Which gives me pretty much what I want except for when the year changes:
week week_num Total Cases
2022-01-10 2 20803473
2022-01-03 1 17217248
**2022-01-01 52 2115780**
**2021-12-27 52 7971560**
2021-12-20 51 5561762
I want to figure out the workaround to combining the '52' values together. Alternatively, my dataset has 112 weeks; can I assign the values 1-112 to the whole dataset rather than 1-52 for each year?
You could aggregate on a correction via a CASE WHEN for the iso year.
SELECT
min(weekstart) as [week]
, [iso_week] as weeknum
, sum(new_cases) as [Total Cases]
FROM
(
SELECT
min([date]) as weekstart
, datepart(iso_week, [date]) as [iso_week]
, case
when month(min([date])) = 1
and datepart(iso_week, [date]) >= 52
then year([date]) - 1
when month(min([date])) = 12
and datepart(iso_week, [date]) = 1
then year([date]) + 1
else year([date])
end as iso_year
, sum(new_cases) as new_cases
FROM covid_data_cleaned
GROUP BY year([date]), datepart(iso_week, [date])
) q
GROUP BY iso_year, [iso_week]
ORDER BY [week] DESC;
Instead of iso week, you can use week.
DECLARE #table table(datev date, new_Cases int)
INSERT INTO #table values
('2022-01-01',123),('2022-01-09',432),('2022-01-03',123),('2021-12-27',234);
SELECT MIN(datev) as week, datepart(week, datev) week_num, sum(new_cases) as [Total Cases]
FROM #table
GROUP BY DATEPART(week, datev), DATEPART(year, datev)
ORDER BY MIN(datev) DESC
week
week_num
Total Cases
2022-01-09
3
432
2022-01-03
2
123
2022-01-01
1
123
2021-12-27
53
234

Pivoting unique users for each month, each year

I'm learning about PIVOT function and I want to try it in my DB, in the table DDOT I have events (rows) made by users during X month Y year in the YYYYMM format.
id_ev iddate id_user ...
------------------------
1 201901 321
2 201902 654
3 201903 987
4 201901 321
5 201903 987
I'm basing my query on the MS Documentation and I'm not getting errors but I'm not able to fill it with the SUM of those unique events (users). In simple words I want to know how many users (unique) checked up each month (x axis) in the year (y axis). However, I'm getting NULL as result
YYYY jan feb mar
----------------------------
2019 NULL NULL NULL
I'm expecting a full table with what I mentionted before.
YYYY jan feb mar
----------------------------
2019 2 1 1
In the code I've tried with different aggregate functions but this block is the closest to a result from SQL.
CREATE TABLE ddot
(
id_ev int NOT NULL ,
iddate int NOT NULL ,
id_user int NOT NULL
);
INSERT INTO DDOT
(
[id_ev], [iddate], [id_user]
)
VALUES
(
1, 201901, 321
),
(
2, 201902, 654
),
(
3, 201903, 987
),
(
4, 201901, 321
),
(
5, 201903, 987
)
GO
SELECT *
FROM (
SELECT COUNT(DISTINCT id_user) [TOT],
DATENAME(YEAR, CAST(iddate+'01' AS DATETIME)) [YYYY], --concat iddate 01 to get full date
DATENAME(MONTH, CAST(iddate+'01' AS DATETIME)) [MMM]
FROM DDOT
GROUP BY DATENAME(YEAR, CAST(iddate+'01' AS DATETIME)),
DATENAME(MONTH, CAST(iddate+'01' AS DATETIME))
) AS DOT_COUNT
PIVOT(
SUM([TOT])
FOR MMM IN (jan, feb, mar)
) AS PVT
Ideally you should be using an actual date in the iddate column, and not a string (number?). We can workaround this using the string functions:
SELECT
CONVERT(varchar(4), LEFT(iddate, 4)) AS YYYY,
COUNT(CASE WHEN CONVERT(varchar(2), RIGHT(iddate, 2)) = '01' THEN 1 END) AS jan,
COUNT(CASE WHEN CONVERT(varchar(2), RIGHT(iddate, 2)) = '02' THEN 1 END) AS feb,
COUNT(CASE WHEN CONVERT(varchar(2), RIGHT(iddate, 2)) = '03' THEN 1 END) AS mar,
...
FROM DDOT
GROUP BY
CONVERT(varchar(4), LEFT(iddate, 4));
Note that if the iddate column already be text, then we can remove all the ugly calls to CONVERT above:
SELECT
LEFT(iddate, 4) AS YYYY,
COUNT(CASE WHEN RIGHT(iddate, 2) = '01' THEN 1 END) AS jan,
COUNT(CASE WHEN RIGHT(iddate, 2) = '02' THEN 1 END) AS feb,
COUNT(CASE WHEN RIGHT(iddate, 2) = '03' THEN 1 END) AS mar,
...
FROM DDOT
GROUP BY
LEFT(iddate, 4);

total Sales count for last current week, month and yearly - SQL

How do you find the total sales count for a salesperson by weekly, monthly and yearly ?
SELECT AdjusterName,
SUM(CASE WHEN TaskDate >= DATEADD(WEEK, DATEDIFF(WEEK, 0, SYSDATETIME()), 0)
AND TaskDate < DATEADD(WEEK, DATEDIFF(WEEK, 0, SYSDATETIME()) + 1, 0)
THEN 1 ELSE 0 END) AS WTD,
sum(case when MONTH(TaskDate) = MONTH(GetDate()) then 1 else 0 end ) as MTD,
sum(case when year(TaskDate) = year(GetDate()) then 1 else 0 end ) as YTD
FROM cte
GROUP BY AdjusterName
for example:-
name WTD MTD YTD
SalesPersnA 2 5 10
Sample Data
ID SalesPerson NAME TaskDate TaskAge DocumentType TaskStatus OverdueCheck
2000378 Willy Akron FNOL Supervisor Team 1 2015-02-04 1258 Claim.Reassigned.File.Text Completed WithinSLA
2000378 Amanda Akron FNOL Supervisor Team 1 2015-02-04 1258 ClaimLifecycle.Open.RD.Text Completed WithinSLA
2000388 Amanda Akron FNOL Supervisor Team 1 2016-08-06 709 ClaimLifecycle.Open.RD.Text Completed WithinSLA
2000388 Willy Akron FNOL Supervisor Team 1 2016-08-06 709 Claim.Reassigned.File.Text Completed WithinSLA
2000388 Schutz Akron FNOL Supervisor Team 1 2016-09-21 663 ISO.Failure.Diary.Text: Completed WithinSLA
2000388 Stephannie Akron FNOL Supervisor Team 1 2016-09-26 658 Claim.Reassigned.File.Text Completed WithinSLA
Count ignore nulls, so you could use a count call on top of a case expression for the last week and month. The condition on the year could be expressed in the where clause:
SELECT SalesPerson,
COUNT(CASE DATEDIFF(WEEK, TaskDate, GETDATE()) = 0 THEN 1 END) AS WTD,
COUNT(CASE DATEDIFF(MONTH, TaskDate, GETDATE()) = 0 THEN 1 END) AS MTD,
COUNT(*)
FROM mytable
WHERE DATEDIFF(YEAR, TaskDate, GETDATE()) = 0
GROUP BY SalesPerson
If you want results for year, month, week in columns you can achieve with window functions:
declare #test table (ID int, SalesPerson nvarchar(100), [Name] nvarchar(100), TaskDate date, TaskAge int, DocumentType nvarchar(max), TaskStatus nvarchar(20), OverdueCheck nvarchar(20))
insert into #test values
(2000378, 'Willy' , 'Akron FNOL Supervisor Team 1', '2015-02-04', 1258, 'Claim.Reassigned.File.Text ', 'Completed', 'WithinSLA')
, (2000378, 'Amanda' , 'Akron FNOL Supervisor Team 1', '2015-02-04', 1258, 'ClaimLifecycle.Open.RD.Text', 'Completed', 'WithinSLA')
, (2000388, 'Amanda' , 'Akron FNOL Supervisor Team 1', '2016-08-06', 709 , 'ClaimLifecycle.Open.RD.Text', 'Completed', 'WithinSLA')
, (2000388, 'Willy' , 'Akron FNOL Supervisor Team 1', '2016-08-06', 709 , 'Claim.Reassigned.File.Text ', 'Completed', 'WithinSLA')
, (2000388, 'Schutz' , 'Akron FNOL Supervisor Team 1', '2016-09-21', 663 , 'ISO.Failure.Diary.Text: ', 'Completed', 'WithinSLA')
, (2000388, 'Stephannie', 'Akron FNOL Supervisor Team 1', '2016-09-26', 658 , 'Claim.Reassigned.File.Text ', 'Completed', 'WithinSLA')
select SalesPerson
, Year(TaskDate) as [Year]
, month(taskdate) as [Month]
, DATEPART( wk, taskdate) as [Week]
, count(*) over (Partition by salesperson, Year(taskdate)) as [YearCount]
, count(*) over (Partition by salesperson, Year(taskdate), Month(taskdate)) as [MonthCount]
, count(*) over (Partition by salesperson, Year(taskdate), DATEPART( wk, taskdate)) as [WeekCount]
from #test
This worked for me! Thank you all for your suggestions and time.
select AdjusterName, NAME,
sum(case when DATEPART( wk,TaskDate) = DATEPART ( wk, getdate()) and YEAR(taskDate) = 2018 then 1 else 0 end ) as WTD,
sum(case when DATEPART (m, TaskDate ) = DATEPART ( m, getdate()) and YEAR(taskDate) = 2018 then 1 else 0 end ) as MTD,
sum(case when DATEPART (YEAR, TaskDate) = DATEPART ( YEAR, getdate()) and YEAR(taskDate) = 2018 then 1 else 0 end ) as YTD
from cte
group by AdjusterName,NAME

Pivot and get count of rows in each cell?

The following query gives me the year and month_num of each support ticket.
SELECT STRFTIME_UTC_USEC(created_at, '%Y') AS year,
STRFTIME_UTC_USEC(created_at, '%m') AS month_num
FROM zendesk.zendesk
I want to pivot the year values and show the COUNT(*) of all source rows in each cell, like this:
2014 2015 2016
01 5 ... ...
02 8
03 12
04 22
05 30
06 15
07 10
08 9
09 ...
10
11
12
How can I do this?
You can use conditional aggregation:
SELECT STRFTIME_UTC_USEC(created_at, '%m') AS month_num,
SUM(CASE WHEN STRFTIME_UTC_USEC(created_at, '%Y') = '2014' then 1 else 0 end) as cnt_2014,
SUM(CASE WHEN STRFTIME_UTC_USEC(created_at, '%Y') = '2015' then 1 else 0 end) as cnt_2015,
SUM(CASE WHEN STRFTIME_UTC_USEC(created_at, '%Y') = '2016' then 1 else 0 end) as cnt_2016
FROM zendesk.zendesk
GROUP BY month_num;
SELECT
month_num,
MIN(CASE WHEN [year] = '2014' THEN cnt END) AS year_2014,
MIN(CASE WHEN [year] = '2015' THEN cnt END) AS year_2015,
MIN(CASE WHEN [year] = '2016' THEN cnt END) AS year_2016
FROM (
SELECT
STRFTIME_UTC_USEC(created_at, '%Y') AS [year],
STRFTIME_UTC_USEC(created_at, '%m') AS month_num,
COUNT(*) AS cnt
FROM zendesk.zendesk
GROUP BY 1,2
)
GROUP BY 1

How to sum total by a field name by month for the previous six month

I have a table with following fields
Status, branch, reason code description, year, month, day, count
I am trying to get a report like this for each branch:
Reason code desc.,July,August,sept.,October.,nov.,Dec.,Total,% of Total
Reason 1. 4. 6. 2. 5. 0. 2. 19 79.1
Reason 2. 1 0. 2. 1. 1. 0 5. 20.9
--------------------------
5. 6. 4. 6. 1. 2. 24 100.0
Assuming that your database is Oracle the query can be:
select RCMonth.*, RCTotal.Total, round(RCTotal.Total*100/Total.Total, 2) "Total%"
from
(
select reasoncode , to_char(createdon,'Mon') mon
from rc_report
where createdon > sysdate - 180
union all
select 'Sum' , to_char(createdon,'Mon') mon
from rc_report
where createdon > sysdate - 180
) pivot
( count(*)
for mon in ('Aug', 'Sep', 'Oct', 'Nov', 'Dec', 'Jan')
) RCMonth
join ( select count(*) Total,
reasoncode
from rc_report
where createdon > sysdate - 180
group by reasoncode
union all
select count(*) Total,
'Sum'
from rc_report
where createdon > sysdate - 180
) RCTotal
on RCTotal.ReasonCode = RCMonth.ReasonCode
cross join (
select count(*) Total
from rc_report
where createdon > sysdate - 180 ) Total
order by RCMonth.ReasonCode
Sample result is:
Row# REASONCODE 'Aug' 'Sep' 'Oct' 'Nov' 'Dec' 'Jan' TOTAL Total%
1 Reason1 2 2 0 3 2 0 9 81.82
2 Reason2 0 1 0 1 0 0 2 18.18
3 Sum 2 3 0 4 2 0 11 100
Table definition is :
create table rc_report
(reasoncode varchar2(20),
createdon date)
And also version for SQL Server in SQL Fiddle
It's only a little bit different.
select RCMonth.*, RCTotal.Total, round( cast ( RCTotal.Total as decimal) *100 /Total.Total,2) "Total%"
from
(
select reasoncode, [Aug], [Sep], [Oct], [Nov], [Dec], [Jan] from (
select reasoncode , left( datename(Month,createdon),3) mon
from rc_report
where createdon >= DATEADD(MONTH, -6, GETDATE())
union all
select 'Sum' , left( datename(Month, createdon),3) mon
from rc_report
where createdon >= DATEADD(MONTH, -6, GETDATE())
) Source
pivot
( count(Source.mon)
for mon in ([Aug], [Sep], [Oct], [Nov], [Dec], [Jan])
) as PivotTable
) RCMonth
join ( select count(*) Total,
reasoncode
from rc_report
where createdon >= DATEADD(MONTH, -6, GETDATE())
group by reasoncode
union all
select count(*) Total,
'Sum'
from rc_report
where createdon >= DATEADD(MONTH, -6, GETDATE())
) RCTotal
on RCTotal.ReasonCode = RCMonth.ReasonCode
cross join (
select count(*) Total
from rc_report
where createdon >= DATEADD(MONTH, -6, GETDATE()) ) Total
order by RCMonth.ReasonCode