I have a table with records dating back 3 years that is continuously updated with new records and back filled for prior weeks.
I would like to create a result set that looks like the example below, where the columns 1,2,3,4, etc represent a specific week of the year. As data gets inserted, I would be able to add more columns for each week. I would like to run each date range every time I run this report because data is often entered for prior dates, so prior weeks will change.
location
name
wk1_hours
wk2_hours
wk3_hours
USA
John S
56.39
14.01
73.55
USA
Alex P
24.36
22.47
NULL
USA
Christine H
NULL
78.27
42.37
Canada
John S
40.32
NULL
22.58
Canada
Eric B
53.00
3.44
27.84
Mexico
Charles T
10.58
8.48
NULL
This is the query I have so far, how do I apply multiple date range variables to this query and to separate columns?
declare #wk1start as datetime
declare #wk1end as datetime
set #wk1start = '2022/7/4 00:00:00'
set #wk1end = '2022/7/10 23:59:59'
select
location,
name,
cast(hours as numeric (36,2)) as wk1_hours
from table
where name is not null
and date_of_service between #wk1start and #wk1end
group by
location
First let's slap together some test data. Providing example DDL and DML is very helpful when you ask questions like this:
DECLARE #TABLE TABLE (Location NVARCHAR(50), Name NVARCHAR(50), Date DATE, Hours INT)
INSERT INTO #TABLE (Location, Name, Date, Hours) VALUES
('USA', 'John S', '2022-12-01', 10),('USA', 'John S', '2022-12-02', 10),('USA', 'John S', '2022-12-03', 10),('USA', 'John S', '2022-12-04', 10),('USA', 'John S', '2022-12-05', 10),
('USA', 'Alex P', '2022-12-01', 5),('USA', 'Alex P', '2022-12-02', 5),('USA', 'Alex P', '2022-12-03', 5),('USA', 'Alex P', '2022-12-04', 5),('USA', 'Alex P', '2022-12-05', 5),
('USA', 'Christine H', '2022-12-01', 5),('USA', 'Christine H', '2022-12-02', 5),('USA', 'Christine H', '2022-12-03', 5),('USA', 'Christine H', '2022-12-04', 5),('USA', 'Christine H', '2022-12-05', 5)
INSERT INTO #TABLE (Location, Name, Date, Hours)
SELECT Location, Name, DATEADD(YEAR, -1, Date), Hours / 2
FROM #TABLE
UNION ALL
SELECT Location, Name, DATEADD(YEAR, -2, Date), Hours / 2
FROM #TABLE
I just threw some off the cuff values in the table, and then manipulated it to get a couple more years for the same dates.
You can use this data to mock up something similar to what you have as the desired output using some aggregation using the DATEPART function and then PIVOT like this:
SELECT a.Location, Week, a.Name, a.WeekOne, a.WeekTwo, a.WeekThree
FROM (
SELECT Location, Name, DATEPART(WEEK,Date) AS Week,
SUM(CASE WHEN DATEPART(YEAR,Date) = 2020 THEN Hours END) OVER (PARTITION BY Location, Name, DATEPART(WEEK,Date) ORDER BY (SELECT 1)) AS WeekOne,
SUM(CASE WHEN DATEPART(YEAR,Date) = 2021 THEN Hours END) OVER (PARTITION BY Location, Name, DATEPART(WEEK,Date) ORDER BY (SELECT 1)) AS WeekTwo,
SUM(CASE WHEN DATEPART(YEAR,Date) = 2022 THEN Hours END) OVER (PARTITION BY Location, Name, DATEPART(WEEK,Date) ORDER BY (SELECT 1)) AS WeekThree
FROM #TABLE
) a
GROUP BY a.Location, a.Name, Week, a.WeekOne, a.WeekTwo, a.WeekThree
Location Name weekNumber weekOne weekTwo weekThree
-------------------------------------------------------------
USA Alex P 49 10 8 15
USA Christine H 49 10 8 15
USA John S 49 25 20 30
This does make some assumptions. You say 'specific week of the year' which I inferred to mean the ISO week number (1-53). If that's not the case, you can group by what ever definition you need instead.
If you're not a fan of PIVOT then you could instead have some fun with windowed functions:
SELECT a.Location, Week, a.Name, a.WeekOne, a.WeekTwo, a.WeekThree
FROM (
SELECT Location, Name, DATEPART(WEEK,Date) AS Week,
SUM(CASE WHEN DATEPART(YEAR,Date) = 2020 THEN Hours END) OVER (PARTITION BY Location, Name, DATEPART(WEEK,Date) ORDER BY (SELECT 1)) AS WeekOne,
SUM(CASE WHEN DATEPART(YEAR,Date) = 2021 THEN Hours END) OVER (PARTITION BY Location, Name, DATEPART(WEEK,Date) ORDER BY (SELECT 1)) AS WeekTwo,
SUM(CASE WHEN DATEPART(YEAR,Date) = 2022 THEN Hours END) OVER (PARTITION BY Location, Name, DATEPART(WEEK,Date) ORDER BY (SELECT 1)) AS WeekThree
FROM #TABLE
) a
GROUP BY a.Location, a.Name, Week, a.WeekOne, a.WeekTwo, a.WeekThree
I think this is a little easier to read, and lends itself to using parameters better than the pivot method, but you're still stuck with manual columns.
Location Week Name WeekOne WeekTwo WeekThree
---------------------------------------------------------
USA 49 Alex P 10 8 15
USA 50 Alex P NULL 2 10
USA 49 Christine H 10 8 15
USA 50 Christine H NULL 2 10
USA 49 John S 25 20 30
USA 50 John S NULL 5 20
Related
table employee {
id,
name
}
table payment_record {
id,
type, // 1 is salary, 2-4 is bonus
employee_id,
date_paid,
amount
}
i want to query employee's newest salary and sum(bonus) from some date.
like my payments is like
id, type, employee_id, date_paid, amount
1 1 1 2022-10-01 5000
2 2 1 2022-10-01 1000
3 3 1 2022-10-01 1000
4 1 1 2022-11-01 3000
5 1 2 2022-10-01 1000
6 1 2 2022-11-01 2000
7 2 2 2022-11-01 3000
query date in ['2022-10-01', '2022-11-01']
show me
employee_id, employee_name, newest_salary, sum(bonus)
1 Jeff 3000 2000
2 Alex 2500 3000
which jeff's newest_salary is 3000 becuase there is 2 type = 1(salary) record 5000 and 3000, the newest one is 3000.
and jeff's bonus sum is 1000(type 2) + 1000(type 3) = 2000
the current sql i try is like
select
e.employee_id,
employee.name,
e.newest_salary,
e.bonus
from
(
select
payment_record.employee_id,
SUM(case when type in ('2', '3', '4') then amount end) as bonus,
Max(case when type = '1' then amount end) as newest_salary
from
payment_record
where
date_paid in ('2022-10-01', '2022-11-01')
group by
employee_id
) as e
join
employee
on
employee.id = e.employee_id
order by
employee_id
it's almost done, but the rule of newest_salary is not correct, i just get the max value althought usually the max value is newest record.
The query is below:
SELECT
t1.id employee_id,
t1.name employee_name,
t3.amount newest_salary,
t2.bonus bonus
FROM employee t1
LEFT JOIN
(
SELECT
employee_id,
MAX(CASE WHEN type=1 THEN date_paid END) date_paid,
SUM(CASE WHEN type IN (2,3,4) THEN amount END) bonus
FROM payment_record
WHERE date_paid BETWEEN '2022-10-01' AND '2022-11-01'
GROUP BY employee_id
) t2
ON t1.id=t2.employee_id
LEFT JOIN payment_record t3
ON t3.type=1 AND
t2.employee_id=t3.employee_id AND
t2.date_paid=t3.date_paid
ORDER BY t1.id
db fiddle
I think Postgres is close enough to work with this solution I tested in sql-server, but it should at least be close enough to translate
My approach is to split the payments in the desired range into salary vs bonus, and sum the bonus but use a partitioned row number to identify the newest salary payment for each employee in the desired date range and only join that one to the bonus totals. Note that I used a LEFT JOIN because an employee might not get a bonus.
DECLARE #StartDate DATE = '2022-10-01';
DECLARE #EndDate DATE = '2022-11-01';
with cteSample as ( --BEGIN sample data
SELECT * FROM ( VALUES
(1, 1, 1, CONVERT(DATE,'2022-10-01'), 5000)
, (2, 2, 1, '2022-10-01', 1000)
, (3, 3, 1, '2022-10-01', 1000)
, (4, 1, 1, '2022-11-01', 3000)
, (5, 1, 2, '2022-10-01', 1000)
, (6, 1, 2, '2022-11-01', 2000)
, (7, 2, 2, '2022-11-01', 3000)
) as TabA(Pay_ID, Pay_Type, employee_id, date_paid, amount)
) --END Sample data
, ctePayments as ( --Filter down to just the payments in the date range you are interested in
SELECT Pay_ID, Pay_Type, employee_id, date_paid, amount
FROM cteSample --Replace this with your real table of payments
WHERE date_paid >= #StartDate AND date_paid <= #EndDate
), cteSalary as ( --Identify salary payments in range and order them newest first
SELECT employee_id, amount
, ROW_NUMBER() over (PARTITION BY employee_id ORDER BY date_paid DESC) as Newness
FROM ctePayments as S
WHERE S.Pay_Type = 1
), cteBonus as ( --Identify bonus payments in range and sum them
SELECT employee_id, SUM(amount) as BonusPaid
FROM ctePayments as S
WHERE S.Pay_Type in (2,3,4)
GROUP BY employee_id
)
SELECT S.employee_id, S.amount as SalaryNewest
, COALESCE(B.BonusPaid, 0) as BonusTotal
FROM cteSalary as S --Join the salary list to the bonusa list
LEFT OUTER JOIN cteBonus as B ON S.employee_id = B.employee_id
WHERE S.Newness = 1 --Keep only the newest
Result:
employee_id
SalaryNewest
BonusTotal
1
3000
2000
2
2000
3000
I have to find PatientName when they have 9 hours or more within a 7 days span. Then provide sum of hours by PatientName. Example:
TempTable1 pulls one year of data:
PatientName VisitDate Hours
Steve 11/2/2016 1
Steve 11/3/2016 1
Pete 11/1/2016 3
Pete 11/2/2016 7
Dave 11/1/2016 3
Dave 11/2/2016 3
Dave 2/2/2016 3
Steve doesn't have enough hours. Dave has 9 hours but are not within 7 days. Pete has 10 hours within 7 days. Therefore output should be like:
PatientName (SUM)Hours
Pete 10
This is simply beyond my ability. I think this will take a multi select with lead and lag, but this is simply beyond my ability level
Hopefully some SQL ninja god can do this. Please save me.
One method is a self-join with aggregation:
select t1.patientname, sum(t2.hours)
from temptable1 t1 join
temptable1 t2
on t2.patientname = t1.patientname and
t2.visitdate >= t1.visitdate and
t2.visitdate < datead(day, 7, t1.visitdate)
group by t1.patientname
having sum(t2.hours) >= 9;
Following query helps you get the desired output:
select patient_name,week,sum(hours) as hours_served from
(
select patient_name,date,hours,week(date) as week
from temptable1
)
group by patient_name,week
having hours_served >=9;
CREATE TABLE #Temp
(
PatientName VARCHAR(32),
VisitDate DATE,
[Hours] INT
)
INSERT #Temp
VALUES
('Steve', CAST('11/2/2016' AS DATE), 1),
('Steve', CAST('11/3/2016' AS DATE), 1),
('Pete', CAST('11/1/2016' AS DATE), 3),
('Pete', CAST('11/2/2016' AS DATE), 7),
('Dave', CAST('11/1/2016' AS DATE), 3),
('Dave', CAST('11/2/2016' AS DATE), 3),
('Dave', CAST('2/2/2016' AS DATE), 3)
SELECT p.PatientName, MAX(p.HoursSum)
FROM (
SELECT t2.PatientName, SUM(t2.[Hours]) AS HoursSum
FROM #Temp t1
JOIN #Temp t2 ON t1.PatientName = t2.PatientName AND t2.VisitDate BETWEEN t1.VisitDate AND DATEADD(DAY, 7, t1.VisitDate)
GROUP BY t2.PatientName, t1.VisitDate
HAVING SUM(t2.[Hours]) >= 9
) p
GROUP BY p.PatientName
DROP TABLE #Temp
state month ID sales
-------------------------------------
FL 05/18/2015 0001 12,000
FL 05/19/2015 0001 6,000
FL 05/20/2015 0001 3,000
FL 05/21/2015 0001 6,000
FL 06/01/2016 0001 4,000
TX 06/02/2016 0050 1,000
In the above table month column having same month but with different date. My question is how to add/sum of sales column having same month with different dates?
try this way
select datepart(year,month) as year ,datepart(month,month) as month, sum(sales)
from tablename
group by datepart(year,month) ,datepart(month,month)
Use a simple aggregation on the month column. You can use the DATENAME function to extract the month given a date.
SELECT YEAR([month]), DATENAME(MONTH, [month]) AS Month,
SUM(sales)
FROM mytable
GROUP BY YEAR([month]), DATENAME(MONTH, [month]);
first see this question on how to convert the month column (assuming it's a string/varchar) to a date.
Next make a sum() of the sales column grouping by the month and year part of the date.
Are you saying for you want the totals for January .. December irrespective of the year?
If yes then you could do a simple agrregate and group by DATEPART(month, theDate)
EG
SELECT STATE, DATEPART(month, [month]), SUM(sales)
FROM yourTable
GROUP BY
STATE, DATEPART(month, [month])
By using the below query you can get the result as month, year wise:
SELECT MONTH([Month]) [Month], YEAR([Month]) [Year], SUM(Sales) TotalSales
FROM TestTable
GROUP BY MONTH([Month]), YEAR([Month])
ORDER BY MONTH([Month]), YEAR([Month])
Actual execution with given sample data:
DECLARE #TestTable TABLE ([State] VARCHAR (20), [Month] DATE, ID VARCHAR(5), Sales INT);
INSERT INTO #TestTable ([State], [Month], ID, Sales)
VALUES
('FL', '05/18/2015', '0001', 12000),
('FL', '05/19/2015', '0001', 6000),
('FL', '05/20/2015', '0001', 3000),
('FL', '05/21/2015', '0001', 6000),
('FL', '06/01/2016', '0001', 4000),
('TX', '06/02/2016', '0050', 1000),
('TX', '05/02/2016', '0050', 1000)
SELECT MONTH([Month]) [Month], YEAR([Month]) [Year], SUM(Sales) TotalSales
FROM #TestTable
GROUP BY MONTH([Month]), YEAR([Month])
ORDER BY MONTH([Month]), YEAR([Month])
Result:
Month Year TotalSales
5 2015 27000
5 2016 1000
6 2016 5000
Below query might help you :-
declare #test111 table
(name varchar(20) NOT NULL,
month date NOT NULL,
col1 int NOT NULL,
sales int NOT NULL)
Insert into #test111
values('FL','05/18/2015',0001,12000),
('FL','05/19/2015',0001,6000),
('FL','05/20/2015',0001,3000),
('FL','05/21/2015',0001,6000),
('FL','06/01/2016',0001,4000),
('TX','06/02/2016',0050,1000)
select month,sum(sales) over (partition by datepart(mm,month)) Sales
from #test111
Output :
month Sales
2015-05-18 27000
2015-05-19 27000
2015-05-20 27000
2015-05-21 27000
2016-06-01 5000
2016-06-02 5000
I have a table in MySQL with these data belows:
DATE Category AMOUNT
2016-1-1 A 12
2016-1-1 B 10
2016-1-2 A 5
2016-1-3 C 1
2016-2-1 A 5
2016-2-1 B 6
2016-2-2 A 7
2016-2-3 C 3
How can I get the result as below:
MONTH TOTAL Category-A Category-B Category-C
2016 Jan 28 17 10 1
2016 Feb 21 12 6 3
If you're using MySQL this would work:
SELECT
DATE_FORMAT(DATE, '%Y %b') AS MONTH,
SUM(AMOUNT) AS TOTAL,
SUM(IF(CATEGORY='A', AMOUNT, 0)) AS `Category-A`,
SUM(IF(CATEGORY='B', AMOUNT, 0)) AS `Category-B`,
SUM(IF(CATEGORY='C', AMOUNT, 0)) AS `Category-C`
FROM your_table
GROUP BY MONTH;
For other database engines you might have to change that a little.
DECLARE #Table1 TABLE
(DATE varchar(8), CAT varchar(1), AMOUNT int)
;
INSERT INTO #Table1
(DATE, CAT, AMOUNT)
VALUES
('2016-1-1', 'A', 12),
('2016-1-1', 'B', 10),
('2016-1-2', 'A', 5),
('2016-1-3', 'C', 1),
('2016-2-1', 'A', 5),
('2016-2-1', 'B', 6),
('2016-2-2', 'A', 7),
('2016-2-3', 'C', 3)
;
Select Months,
[A][Category-A],
[B][Category-B],
[C][Category-C],
SUM([A]+[B]+[C])TOTAL
from (
select CAST(year(DATE) AS VARCHAR)+' '+CONVERT(CHAR(3), DATENAME(MONTH, date))Months,
CAT,
AMOUNT
from #Table1)T
PIVOT (SUM(AMOUNT) FOR cat IN ([A],[B],[C]))P
GROUP BY Months,[A],[B],[C]
ORDER BY CONVERT(CHAR(3), DATENAME(MONTH, Months)) desc
Below is a simple query and the result: Is the a way to aggregate the total EVENTs by 7 days, then sum up the total EVENTs? Would a rollup function work? I am using SQL SERVER 05 & 08. Thanks again, folks.
SELECT DATE_SOLD, count(DISTINCT PRODUCTS) AS PRODUCT_SOLD
FROM PRODUCTS
WHERE DATE >='10/1/2009'
and DATE <'10/1/2010'
GROUP BY DATE_SOLD
RESULTS:
DATE_SOLD PRODUCT_SOLD
10/1/09 5
10/2/09 11
10/3/09 14
10/4/09 6
10/5/09 11
10/6/09 13
10/7/09 10
Total 70
10/8/09 4
10/9/09 11
10/10/09 8
10/11/09 4
10/12/09 7
10/13/09 4
10/14/09 9
Total 47
Not having your table design to work with here's what I think you are after (although I have to admit the output needs to be cleaned up). It should, at least, get you some way to the solution you are looking for.
CREATE TABLE MyTable(
event_date date,
event_type char(1)
)
GO
INSERT MyTable VALUES ('2009-1-01', 'A')
INSERT MyTable VALUES ('2009-1-11', 'B')
INSERT MyTable VALUES ('2009-1-11', 'C')
INSERT MyTable VALUES ('2009-1-20', 'N')
INSERT MyTable VALUES ('2009-1-20', 'N')
INSERT MyTable VALUES ('2009-5-23', 'D')
INSERT MyTable VALUES ('2009-5-23', 'E')
INSERT MyTable VALUES ('2009-5-10', 'F')
INSERT MyTable VALUES ('2009-5-10', 'F')
GO
WITH T AS (
SELECT DATEPART(MONTH, event_date) event_month, event_date, event_type
FROM MyTable
)
SELECT CASE WHEN (GROUPING(event_month) = 0)
THEN event_month ELSE '99' END AS event_month,
CASE WHEN (GROUPING(event_date) = 1)
THEN '9999-12-31' ELSE event_date END AS event_date,
COUNT(DISTINCT event_type) AS event_count
FROM T
GROUP BY event_month, event_date WITH ROLLUP
ORDER BY event_month, event_date
This gives the following output:
event_month event_date event_count
1 2009-01-01 1
1 2009-01-11 2
1 2009-01-20 1
1 9999-12-31 4
5 2009-05-10 1
5 2009-05-23 2
5 9999-12-31 3
99 9999-12-31 7
Where the '99' for month and '9999-12-31' for year are the totals.
SELECT DATEDIFF(week, 0, DATE_SOLD) Week,
DATEADD(week, DATEDIFF(week, 0, DATE_SOLD), 0) From,
DATEADD(week, DATEDIFF(week, 0, DATE_SOLD), 0) + 6 To,
COUNT(DISTINCT PRODUCTS) PRODUCT_SOLD
FROM dbo.PRODUCTS
WHERE DATE >= '2009-10-01'
AND DATE < '2010-10-01'
GROUP BY DATEDIFF(week, 0, DATE_SOLD) WITH ROLLUP
ORDER BY DATEDIFF(week, 0, DATE_SOLD)