SQL Server Group BY weekly clause blank if no data - sql

No. 1 Query group by weekly
SELECT
DATEADD(week, DATEDIFF(week, 0, DailyDate), 0) AS WeekStart,
SUM(Soil) AS TotalSoil,
SUM(Rock) AS TotalRock,
SUM(Concrete) AS TotalConcrete,
SUM(Steel) AS TotalSteel,
SUM(MovementPiles) AS TotalMovementPiles,
SUM(Total) AS OverallTotal
FROM
BplPjtRevenueStreams
WHERE
DailyDate > '2016-11-28 00:00:00.000'
AND DailyDate < '2016-12-05 00:00:00.000'
AND Project_Id = 5
GROUP BY
DATEADD(week, DATEDIFF(week, 0, DailyDate), 0)
No. 2 query which have empty data in the table since table has no project_Id = 6 yet
SELECT
DATEADD(week, DATEDIFF(week, 0, DailyDate), 0) AS WeekStart,
SUM(Soil) AS TotalSoil,
SUM(Rock) AS TotalRock,
SUM(Concrete) AS TotalConcrete,
SUM(Steel) AS TotalSteel,
SUM(MovementPiles) AS TotalMovementPiles,
SUM(Total) AS OverallTotal
FROM
BplPjtRevenueStreams
WHERE
DailyDate > '2016-11-28 00:00:00.000'
AND DailyDate < '2016-12-05 00:00:00.000'
AND Project_Id = 6
GROUP BY
DATEADD(week, DATEDIFF(week, 0, DailyDate), 0)
May I know if I want to do something like this for the No. 2 query?
Week Start TotalSoil TotalRock TotalConcrete TotalSteel TotalMovementPiles OverallTotal
1 2016-11-28 00:00:00.000 NULL NULL NULL NULL NULL NULL
1 2016-12-05 00:00:00.000 NULL NULL NULL NULL NULL NULL
Is it possible to do like this if it's empty result in the table?

DECLARE
#Project_Id int = 6,
#RangeFrom datetime = '2016-11-28 00:00:00.000',
#RangeTo datetime = '2016-12-05 00:00:00.000'
;WITH Numbers AS
(
SELECT * FROM (VALUES(1),(2),(3),(4),(5),(6),(7),(8),(9),(10)) t(Value)
), Weeks AS
(
-- All weeks start from year 2000, Monday
SELECT DATEADD(WEEK, ROW_NUMBER() OVER (ORDER BY (SELECT NULL)), '1999-12-27') AS DateValue
FROM Numbers a CROSS JOIN Numbers b CROSS JOIN Numbers c CROSS JOIN Numbers d
), Data AS
(
-- This is your query
SELECT
DATEADD(week, DATEDIFF(week, 0, DailyDate), 0) AS WeekStart,
SUM(Soil) AS TotalSoil,
SUM(Rock) AS TotalRock,
SUM(Concrete) AS TotalConcrete,
SUM(Steel) AS TotalSteel,
SUM(MovementPiles) AS TotalMovementPiles,
SUM(Total) AS OverallTotal
FROM
BplPjtRevenueStreams
WHERE
DailyDate > #RangeFrom
AND DailyDate < #RangeTo
AND Project_Id = #Project_Id
GROUP BY
DATEADD(week, DATEDIFF(week, 0, DailyDate), 0)
)
SELECT
CAST(w.DateValue AS datetime) AS WeekStart,
d.TotalSoil,
d.TotalRock,
d.TotalConcrete,
d.TotalSteel,
d.TotalMovementPiles,
d.OverallTotal
FROM Weeks w LEFT OUTER JOIN Data d ON w.DateValue = CAST(d.WeekStart AS date)
WHERE
w.DateValue BETWEEN CAST(#RangeFrom AS date) AND CAST(#RangeTo AS date)

Related

Issue with TSQL Query only returns months that have values and not 0 as needed

The following TSQL returns last 6 month count of data, but if a month does not have records it does not just return 0 as I need it to do, how can I make it do so?
declare #thismonth as Date = DateAdd(d, 1 - DatePart(d, CURRENT_TIMESTAMP), CURRENT_TIMESTAMP);
declare #firstmonth as Date = DateAdd(m, -6, #thismonth);
SELECT YEAR(StartDate) year,
MONTH(StartDate) month,
COUNT(StartDate) EmployeeStartet
FROM
EFP_EmploymentUser
WHERE StartDate BETWEEN #firstmonth AND #thismonth
GROUP BY YEAR(StartDate), MONTH(StartDate)
ORDER BY YEAR(StartDate) DESC, MONTH(StartDate) DESC;
You could do something like this:
declare #lastMonth as Date = DateAdd(d, 1 - DatePart(d, CURRENT_TIMESTAMP), CURRENT_TIMESTAMP);
declare #firstMonth as Date = DateAdd(m, -6, #lastMonth);
WITH months AS (
SELECT #firstMonth AS thisMonth
UNION ALL
SELECT DateAdd(m, 1, thisMonth) AS thisMonth
FROM months
WHERE thisMonth < #lastMonth
),
data AS (
-- your sql here
SELECT YEAR(StartDate) year,
MONTH(StartDate) month,
COUNT(StartDate) EmployeeStartet
FROM
EFP_EmploymentUser
WHERE StartDate BETWEEN #firstmonth AND #thismonth
GROUP BY YEAR(StartDate), MONTH(StartDate)
)
SELECT
YEAR(m.thisMonth) AS year,
MONTH(m.thisMonth) AS month,
ISNULL(d.EmployeeStartet, 0) AS EmployeeStartet
FROM months m
LEFT OUTER JOIN data d
ON
d.year = YEAR(m.thisMonth)
AND d.month = MONTH(m.thisMonth)
ORDER BY m.thisMonth DESC
Left join your data to a tally
declare #thismonth as Date = DateAdd(d, 1 - DatePart(d, CURRENT_TIMESTAMP), CURRENT_TIMESTAMP);
declare #firstmonth as Date = DateAdd(m, -6, #thismonth);
with m as (
select #firstMonth md union
select DateAdd(m, 1, #firstMonth) union
select DateAdd(m, 2, #firstMonth) union
select DateAdd(m, 3, #firstMonth) union
select DateAdd(m, 4, #firstMonth) union
select DateAdd(m, 5, #firstMonth)
),
ym as (
select YEAR(md) y, MONTH(md) m
from m
)
select ym.y, ym.m, COUNT(e.StartDate) EmployeeStartet
from ym
left join EFP_EmploymentUser e
ON (e.StartDate BETWEEN #firstmonth AND #thismonth)
AND ym.y = YEAR(e.StartDate) AND ym.m = MONTH(e.StartDate)
GROUP BY ym.y, ym.m
ORDER BY ym.y DESC, ym.m DESC;

SQL Server grouped rows return with default values if no row available for a date period

I'm trying to write a stored procedure which groups up rows based on their month and return a sum of all items if they exist and 0 if they don't.
For the date part of the query, what I am trying to get is today's date - extract the month and go back 5 months to gather any data if it exists.
At this stage, the query runs fine as is but I'm wondering if there's any way to optimise this as it looks like I'm running the same set of data over and over again and also it's hard coded to an extent.
The dataset I am trying to achieve is as follows:
Month TotalAmount TotalCount
-----------------------------------
2017-11 0 0
2017-12 200.00 2
2018-01 300.00 3
2018-02 0 0
2018-03 300.00 3
2018-04 100.00 1
Using the following query below, I was able to achieve what I want but as you can see, it's hard coding back the past 5 months so if I wanted to go back 12 months, I'd have to add in more code.
DECLARE #5MonthAgo date = CAST(DATEADD(MONTH, -5, GETDATE()) + 1 - DATEPART(DAY, DATEADD(MONTH, -5, GETDATE())) AS DATE)
DECLARE #4MonthAgo date = CAST(DATEADD(MONTH, -4, GETDATE()) + 1 - DATEPART(DAY, DATEADD(MONTH, -4, GETDATE())) AS DATE)
DECLARE #3MonthAgo date = CAST(DATEADD(MONTH, -3, GETDATE()) + 1 - DATEPART(DAY, DATEADD(MONTH, -3, GETDATE())) AS DATE)
DECLARE #2MonthAgo date = CAST(DATEADD(MONTH, -2, GETDATE()) + 1 - DATEPART(DAY, DATEADD(MONTH, -2, GETDATE())) AS DATE)
DECLARE #1MonthAgo date = CAST(DATEADD(MONTH, -1, GETDATE()) + 1 - DATEPART(DAY, DATEADD(MONTH, -1, GETDATE())) AS DATE)
DECLARE #CurrentMonth date = CAST(GETDATE() + 1 - DATEPART(DAY, GETDATE()) AS DATE)
-- Table to return grouped and sum data
DECLARE #StatsTable TABLE ([Month] DATE,
[Total Amount] DECIMAL(18,2),
[Total Count] INT
)
-- Temporary table to hold onto data batch - so table isn't used later on
DECLARE #TempGenTable TABLE ([Id] INT,
[Date] DATETIME,
[Lines] INT NULL,
[Amount] DECIMAL(18, 2) NULL
)
INSERT INTO #TempGenTable
SELECT
Id, Date, Lines, Amount
FROM
TallyTable
WHERE
Date >= #5MonthAgo
INSERT INTO #StatsTable
SELECT
#5MonthAgo,
COALESCE((SELECT SUM(Amount)
FROM #TempGenTable
WHERE Date >= #5MonthAgo AND Date < #4MonthAgo
GROUP BY DATEADD(MONTH, DATEDIFF(MONTH, 0, Date), 0)), 0),
COALESCE((SELECT COUNT(Id)
FROM #TempGenTable
WHERE Date >= #5MonthAgo AND Date < #4MonthAgo
GROUP BY DATEADD(MONTH, DATEDIFF(MONTH, 0, Date), 0)), 0)
UNION
SELECT
#4MonthAgo,
COALESCE((SELECT SUM(Amount)
FROM #TempGenTable
WHERE Date >= #4MonthAgo AND Date < #3MonthAgo
GROUP BY DATEADD(MONTH, DATEDIFF(MONTH, 0, Date), 0)), 0),
COALESCE((SELECT COUNT(Id)
FROM #TempGenTable
WHERE Date >= #4MonthAgo AND Date < #3MonthAgo
GROUP BY DATEADD(MONTH, DATEDIFF(MONTH, 0, Date), 0)), 0)
...
Is there an easier way to be able to get the above data with more flexibility in the number of months?
Is it better to just have the query pass in a month variable and it checks just the current month and have a loop within the controller to go back x number of months?
I would generate the data using a recursive CTE and then use left join:
with months as (
select datefromparts(year(getdate()), month(getdate()), 1) as month_start, 5 as n
union all
select dateadd(month, -1, month_start), n - 1
from months
where n > 0
)
select m.month_start, count(s.id), sum(s.amount)
from months m left join
#StatsTable s
on m.month_start = s.month
group by m.month_start
order by m.month_start;
You haven't provided sample data, so I'm not sure what s.month looks like. You might want the join condition to be:
on s.month >= m.month_start and s.month < dateadd(month, 1, m.month_start)
Below is a set-based method to generate the needed monthly periods:
--sample data
CREATE TABLE dbo.TallyTable (
Id int
, Date datetime
, Lines int
, Amount decimal(18, 2)
);
INSERT INTO dbo.TallyTable
VALUES
(1, '2017-12-05', 1, 50.00)
,(2, '2017-12-06', 1, 150.00)
,(3, '2018-01-10', 1, 100.00)
,(4, '2018-01-11', 1, 100.00)
,(5, '2018-01-12', 1, 100.00)
,(6, '2018-03-15', 1, 225.00)
,(7, '2018-03-15', 1, 25.00)
,(8, '2018-03-15', 1, 50.00)
,(9, '2018-04-20', 1, 100.00);
GO
DECLARE #Months int = 5; --number of historical months
WITH
t10 AS (SELECT n FROM (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) t(n))
,t100 AS (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 0)) - 1 AS num FROM t10 AS a CROSS JOIN t10 AS b)
, periods AS (SELECT
CONVERT(varchar(7), DATEADD(month, DATEDIFF(month, '', GETDATE()) - num, ''),121) AS Month
, DATEADD(month, DATEDIFF(month, '', CAST(GETDATE() AS date)) - num, '') AS PeriodStart
, DATEADD(month, DATEDIFF(month, '', CAST(GETDATE() AS date)) - num + 1, '') AS NextPeriodStart
FROM t100
WHERE num <= #Months
)
SELECT periods.Month, COALESCE(SUM(Amount), 0) AS TotalAmount, COALESCE(COUNT(ID), 0) AS TotalCount
FROM periods
LEFT JOIN dbo.TallyTable ON
TallyTable.Date >= PeriodStart
AND TallyTable.Date < NextPeriodStart
GROUP BY periods.Month
ORDER BY periods.Month;

SQL Query | Summarize TimeSeries

Wondering if anyone can help me out. I have a simple table with the following fields.
ID (int),
TimeStamp (DateTime),
Status (NvarChar)
I need to produce a table with a Count of all Status' for the last 2 hours in 10 minute slots like the example provided. The idea is to produce a Google Line Chart in a dashboard where it will refresh every 10 minutes.
Example:
Table
any help would be appreciated.
José
you could create your time slots with a recursive cte and join to that.
with cte as (
select DATETIMEFROMPARTS(datepart(year,getdate()), datepart(month,getdate()), datepart(day,getdate()), datepart(hour, getdate()), floor((datepart(minute, getdate()) - 9) / 10) * 10, 0, 0) as startDT,
DATETIMEFROMPARTS(datepart(year,getdate()), datepart(month,getdate()), datepart(day,getdate()), datepart(hour, getdate()), floor((datepart(minute, getdate()) + 9) / 10) * 10, 0, 0) as endDT
union all
select DATEADD(minute, -10, startDT),
DATEADD(minute, -10, endDt)
from cte
where DATEADD(minute, -130, getdate()) < DATEADD(minute, -10, startDT)
)
select endDt as [Period],
count(case when [Status] = 'OK' then 1 end) as Status_OK,
count(case when [Status] <> 'OK' then 1 end) as Status_NOK
from cte
left join myTable on [TimeStamp] >= startDT and [TimeStamp] < endDT
group by endDT
if you prefer to use dateadd then
;with cte as (
select dateadd(minute, datediff(minute, 0, getdate()) / 10 * 10, 0) as startDT,
dateadd(minute, datediff(minute, 0, getdate()) / 10 * 10 + 10, 0) as endDT
union all
select dateadd(minute, -10, startDT),
dateadd(minute, -10, endDt)
from cte
where dateadd(minute, -130, getdate()) < dateadd(minute, -10, startDT)
)
select endDt as [Period],
count(case when [Status] = 'OK' then 1 end) as Status_OK,
count(case when [Status] <> 'OK' then 1 end) as Status_NOK
from cte
left join myTable on [TimeStamp] >= startDT and [TimeStamp] < endDT
group by endDT

start and end dates of week in SQL Server for the month

we need to get week start and end dates for the month output shown as below:
Week# StartDate EndDate
Week 1 2017-03-01 2017-03-04
Week 2 2017-03-05 2017-03-11
Week 3 2017-03-12 2017-03-18
Week 4 2017-03-19 2017-03-25
Week 5 2017-03-26 2017-03-31
This should work:
declare #first_day_of_month date = '20170301'
declare #days_in_month int = datediff(day, #first_day_of_month, dateadd(month, 1, #first_day_of_month))
;with x as (
select datepart(week, dateadd(day, n-1, #first_day_of_month))+1 wk, dateadd(day, n-1, #first_day_of_month) dy
from (values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10), (11),(12),(13),(14),(15),(16),(17),(18),(19),(20),(21),(22),(23),(24),(25),(26),(27),(28),(29),(30), (31)) as numbers(n)
where n <= #days_in_month
)
select wk - datepart(week, #first_day_of_month) as [Week#], min(dy) as StartDate, max(dy) as EndDate
from x
group by wk
order by wk
If you have Numbers table in the database, you can get rid of the values.
How about this:
Declare #StartDate Date = '2017-03-01'
;With Nums
AS
(
SELECT *
FROM (VALUES (0),(1),(2),(3),(4)) Nums(n)
),
StartDate
AS
(
SELECT #StartDate As StartDate, DatePart(dw,#StartDate) As DayWeekNumber
)
SELECT DATEADD(Week, n, CASE WHEN n = 0 THEN StartDate ELSE DATEADD(Day, DayWeekNumber * -1 + 1, StartDate) END) As StartDate,
CASE WHEN DATEPART(Month, DATEADD(Week, n+1, DATEADD(Day, DayWeekNumber * -1 , StartDate))) = DATEPART(Month, #StartDate)
THEN DATEADD(Week, n+1, DATEADD(Day, DayWeekNumber * -1 , StartDate))
ELSE EOMONTH(#StartDate) END As EndDate
FROM Nums
CROSS JOIN StartDate
Option 1: Non-UDF version
Declare #Date1 date = '2017-03-01'
Declare #Date2 date = '2017-03-31'
Select [Week#]
,StartDate = min(D)
,EndDate = max(D)
From (
Select *,[Week#] = concat('Week ',Dense_Rank() over (Partition By Year(D),Month(D) Order By DatePart(WK,D)))
From (Select Top (DateDiff(DD,#Date1,#Date2)+1) D=DateAdd(DD,-1+Row_Number() Over (Order By Number),#Date1) From master..spt_values ) A1
) A
Group By [Week#],Year(D),Month(D)
Order By 2
Option 2: TFV Version
I'll often use a TVF to create dynamic date/time ranges. A tally/calendar table would do the trick as well, but the UDF offers some dynamic options. You supply the Date Range, DatePart and Increment
Declare #Date1 date = '2017-03-01'
Declare #Date2 date = '2017-03-31'
Select [Week#]
,StartDate = min(RetVal)
,EndDate = max(RetVal)
From (
Select *,[Week#] = concat('Week ',Dense_Rank() over (Partition By Year(RetVal),Month(RetVal) Order By DatePart(WK,RetVal)))
From [dbo].[udf-Range-Date](#Date1,#Date2,'DD',1)
) A
Group By [Week#],Year(RetVal),Month(RetVal)
Order By 2
Both would Return
Week# StartDate EndDate
Week 1 2017-03-01 2017-03-04
Week 2 2017-03-05 2017-03-11
Week 3 2017-03-12 2017-03-18
Week 4 2017-03-19 2017-03-25
Week 5 2017-03-26 2017-03-31
The UDF if interested
CREATE FUNCTION [dbo].[udf-Range-Date] (#R1 datetime,#R2 datetime,#Part varchar(10),#Incr int)
Returns Table
Return (
with cte0(M) As (Select 1+Case #Part When 'YY' then DateDiff(YY,#R1,#R2)/#Incr When 'QQ' then DateDiff(QQ,#R1,#R2)/#Incr When 'MM' then DateDiff(MM,#R1,#R2)/#Incr When 'WK' then DateDiff(WK,#R1,#R2)/#Incr When 'DD' then DateDiff(DD,#R1,#R2)/#Incr When 'HH' then DateDiff(HH,#R1,#R2)/#Incr When 'MI' then DateDiff(MI,#R1,#R2)/#Incr When 'SS' then DateDiff(SS,#R1,#R2)/#Incr End),
cte1(N) As (Select 1 From (Values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) N(N)),
cte2(N) As (Select Top (Select M from cte0) Row_Number() over (Order By (Select NULL)) From cte1 a, cte1 b, cte1 c, cte1 d, cte1 e, cte1 f, cte1 g, cte1 h ),
cte3(N,D) As (Select 0,#R1 Union All Select N,Case #Part When 'YY' then DateAdd(YY, N*#Incr, #R1) When 'QQ' then DateAdd(QQ, N*#Incr, #R1) When 'MM' then DateAdd(MM, N*#Incr, #R1) When 'WK' then DateAdd(WK, N*#Incr, #R1) When 'DD' then DateAdd(DD, N*#Incr, #R1) When 'HH' then DateAdd(HH, N*#Incr, #R1) When 'MI' then DateAdd(MI, N*#Incr, #R1) When 'SS' then DateAdd(SS, N*#Incr, #R1) End From cte2 )
Select RetSeq = N+1
,RetVal = D
From cte3,cte0
Where D<=#R2
)
/*
Max 100 million observations -- Date Parts YY QQ MM WK DD HH MI SS
Syntax:
Select * from [dbo].[udf-Range-Date]('2016-10-01','2020-10-01','YY',1)
Select * from [dbo].[udf-Range-Date]('2016-01-01','2017-01-01','MM',1)
*/
For a one-off use, this would work:
rextester: http://rextester.com/WCMZRW61517
declare #FromDate date = '20170301';
with n as (select n from (values(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) t(n))
, d as (
select DateValue=convert(date,dateadd(day
, row_number() over (order by (select 1)) -1, #FromDate))
from n as deka
cross join n as hecto
)
select
[Week#] = dense_rank() over (
partition by dateadd(month, datediff(month, 0, DateValue) , 0)
order by convert(tinyint,datepart(week,DateValue))
)
, StartDate = min(DateValue)
, EndDate = max(DateValue)
from d
where dateadd(month, datediff(month, 0, DateValue) , 0) = #FromDate
group by
dateadd(month, datediff(month, 0, DateValue) , 0)
, convert(tinyint,datepart(week,DateValue))
If you want to have a calendar table you can reference as needed, something like this would work:
if object_id('dbo.Calendar') is not null drop table dbo.Calendar;
create table dbo.Calendar (
[Date] date not null
, [Year] smallint not null
, [Month] tinyint not null
, MonthStart date not null
, MonthEnd date not null
, [Week] tinyint not null
, MonthWeek tinyint not null
, MonthWeekStart date not null
, MonthWeekEnd date not null
, constraint pk_Calendar primary key clustered (date)
);
declare #FromDate date = '20170101';
declare #ThruDate date = '20171231';
with n as (select n from (values(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) t(n))
, d as (
select DateValue=convert(date,dateadd(day
, row_number() over (order by (select 1)) -1, #fromdate))
from n as deka
cross join n as hecto
cross join n as kilo /* 2.73 years */
cross join n as [tenK] /* 27.3 years */
--cross join n as [hundredk] /* 273 years */
)
insert into dbo.Calendar
([Date], [Year], [Month],MonthStart, MonthEnd, [Week]
, MonthWeek, MonthWeekStart, MonthWeekEnd)
select top (datediff(day, #FromDate, #ThruDate)+1)
[Date] = DateValue
, [Year] = convert(smallint,datepart(year,DateValue))
, [Month] = convert(tinyint,datepart(month,DateValue))
, MonthStart = dateadd(month, datediff(month, 0, DateValue) , 0)
, MonthEnd = convert(date,dateadd(day,-1
,dateadd(Month,datediff(Month,0,DateValue) +1,0) ) )
, [Week] = convert(tinyint,datepart(week,DateValue))
, MonthWeek = dense_rank() over (
partition by dateadd(month, datediff(month, 0, DateValue) , 0)
order by convert(tinyint,datepart(week,DateValue))
)
, MonthWeekStart = min(DateValue) over (
partition by dateadd(month, datediff(month, 0, DateValue) , 0)
, convert(tinyint,datepart(week,DateValue))
)
, MonthWeekEnd = max(DateValue) over (
partition by dateadd(month, datediff(month, 0, DateValue) , 0)
, convert(tinyint,datepart(week,DateValue))
)
from d
order by DateValue;
select distinct
MonthWeek
, MonthWeekStart
, MonthWeekEnd
from dbo.Calendar
where MonthStart = '20170301'
Calendar and Numbers Tables
Generate a set or sequence without loops - Aaron Bertrand
Creating a Date Table/Dimension in SQL Server 2008 - David Stein
Calendar Tables - Why You Need One - David Stein
Creating a date dimension or calendar table in SQL Server - Aaron Bertrand
TSQL Function to Determine Holidays in SQL Server - Tim Cullen
F_TABLE_DATE - Michael Valentine Jones

SQL Reporting Aggregates and Joining Issues

I am attempting to create a report for orders moving through the various actions on a weekly basis. Could you guys help me out with it?
The desired report (simple ...right):
Week Of Action 1 Action 2 Action 3
---------- -------- -------- --------
2011-11-07 34 55 35
2011-11-14 34 55 35
And here is what I have tried so far, all of which have been miserable failures:
CREATE TABLE WorkOrderHistory
(
WorkOrderHistoryID int, --PK
WorkOrderActionID int,
DateCompleted datetime
)
CREATE TABLE WorkOrderAction
(
WorkOrderActionID int --PK
)
DECLARE
#StartDate DateTime,
#EndDate DateTime,
#SeasonID int;
SET #SeasonID = 16;
SELECT #StartDate = '2011-09-01',
#EndDate = ISNULL((SELECT TOP 1 StartDate FROM Season WHERE SeasonID > #SeasonID), GETDATE()) -- End date will be set to the current date if no season exists beyond the current
--Method 3 Inner Joins. This fails because of my attempt to join on the WeekOf alias to the DATEADD function
SELECT DATEADD(WEEK, DATEDIFF(WEEK, '19000101', WOH.DateCompleted), '19000101') AS WeekOf,
ArtworkCapture.WOsProcessed AS ArtworkCapture
FROM WorkOrderHistory WOH (NOLOCK)
INNER JOIN WorkOrderAction WOA (NOLOCK) ON WOH.WorkOrderActionID = WOA.WorkOrderActionID
INNER JOIN (SELECT COUNT (*) AS WOsProcessed,
DATEADD(WEEK, DATEDIFF(WEEK, '19000101', WOH.DateCompleted), '19000101') AS WeekOf
FROM WorkOrderHistory WOH (NOLOCK) INNER JOIN WorkOrderAction WOA (NOLOCK) ON WOH.WorkOrderActionID = WOA.WorkOrderActionID
WHERE WOH.DateCompleted >= #StartDate AND WOH.DateCompleted < #EndDate
AND WOH.WorkOrderActionID = 1 --Artwork Capture
GROUP BY DATEADD(WEEK, DATEDIFF(WEEK, '19000101', WOH.DateCompleted), '19000101')) ArtworkCapture ON WOH.WeekOf = ArtworkCapture.WeekOf
WHERE WOH.DateCompleted >= #StartDate AND WOH.DateCompleted < #EndDate
GROUP BY DATEADD(WEEK, DATEDIFF(WEEK, '19000101', WOH.DateCompleted), '19000101')
ORDER BY DATEADD(WEEK, DATEDIFF(WEEK, '19000101', WOH.DateCompleted), '19000101')
--Method 2 Subqueries. I can not figure out how to properly form this query.
SELECT DATEADD(WEEK, DATEDIFF(WEEK, '19000101', WOH.DateCompleted), '19000101') AS WeekOf,
(SELECT COUNT (*)
FROM WorkOrderHistory WOH (NOLOCK) INNER JOIN WorkOrderAction WOA (NOLOCK) ON WOH.WorkOrderActionID = WOA.WorkOrderActionID
WHERE WOH.DateCompleted >= #StartDate AND WOH.DateCompleted < #EndDate
AND WOH.WorkOrderActionID = 1 --Artwork Capture
GROUP BY DATEADD(WEEK, DATEDIFF(WEEK, '19000101', WOH.DateCompleted), '19000101'))
AS ArtworkCapture,
(SELECT COUNT (*)
FROM WorkOrderHistory WOH (NOLOCK) INNER JOIN WorkOrderAction WOA (NOLOCK) ON WOH.WorkOrderActionID = WOA.WorkOrderActionID
WHERE WOH.DateCompleted >= #StartDate AND WOH.DateCompleted < #EndDate
AND WOH.WorkOrderActionID = 3 --Art Entry
GROUP BY DATEADD(WEEK, DATEDIFF(WEEK, '19000101', WOH.DateCompleted), '19000101'))
AS ArtEntry
FROM WorkOrderHistory WOH (NOLOCK) INNER JOIN WorkOrderAction WOA (NOLOCK) ON WOH.WorkOrderActionID = WOA.WorkOrderActionID
WHERE WOH.DateCompleted >= #StartDate AND WOH.DateCompleted < #EndDate
GROUP BY DATEADD(WEEK, DATEDIFF(WEEK, '19000101', WOH.DateCompleted), '19000101')
ORDER BY DATEADD(WEEK, DATEDIFF(WEEK, '19000101', WOH.DateCompleted), '19000101')
--This query gives me all of the data I need but it is not aggregated, so there is a record for each action per week so [2011-11-07 - 1 - 34], [2011-11-14 - 1 - 34], [2011-11-07 - 2 - 55], [2011-11-14 - 1 - 55].
SELECT DATEADD(WEEK, DATEDIFF(WEEK, '19000101', WOH.DateCompleted), '19000101') AS WeekOf,
WOA.WorkOrderActionID, COUNT (*) AS WorkOrdersProcessed
FROM WorkOrderHistory WOH (NOLOCK) INNER JOIN WorkOrderAction WOA (NOLOCK) ON WOH.WorkOrderActionID = WOA.WorkOrderActionID
WHERE WOH.DateCompleted >= #StartDate AND WOH.DateCompleted < #EndDate
GROUP BY DATEADD(WEEK, DATEDIFF(WEEK, '19000101', WOH.DateCompleted), '19000101'), WOA.WorkOrderActionID
ORDER BY DATEADD(WEEK, DATEDIFF(WEEK, '19000101', WOH.DateCompleted), '19000101'), WOA.WorkOrderActionID
Looks Like you want to convert rows into columns. You don't need to use sub queries use either Pivot (for SQL 2005+) or SUM/CASE.
Notes: I didn't bother with the Week Calculation since you already have that. I also added a name for WorkOrderAction, since I assume you have that rather than just an ID
Declare #WorkOrderHistory as table
(
WorkOrderHistoryID int, --PK
WorkOrderActionID int,
DateCompleted datetime
)
declare #WorkOrderAction as TABLE
(
WorkOrderActionID int, --PK
Name varchar (50)
)
Insert into #WorkOrderAction
Values (1, 'Action1'),
(2, 'Action2'),
(3, 'Action3')
INSERT INTO #WorkOrderHistory
VALUES (1, 1, '2011-11-07'),
(2, 1, '2011-11-07'),
(3, 1, '2011-11-07'),
(4, 2, '2011-11-07'),
(5, 3, '2011-11-07'),
(6, 3, '2011-11-07'),
(8, 2, '2011-11-14'),
(9, 2, '2011-11-14'),
(10, 2, '2011-11-14'),
(11, 3, '2011-11-14'),
(12, 3, '2011-11-14'),
(13, 1, '2011-11-14')
SELECT datecompleted,
action1,
action2,
action3
FROM (SELECT woa.name,
COUNT(woh.workorderhistoryid) AS kount,
datecompleted
FROM #WorkOrderHistory woh
INNER JOIN #workOrderAction woa
ON woh.workorderactionid = woa.workorderactionid
GROUP BY woa.name,
datecompleted ) p PIVOT ( SUM(kount) FOR p.name IN (
[Action1],
[Action2], [Action3] ) ) AS pvt ​
Produces this result
datecompleted action1 action2 action3
------------------ ------- ------- -------
2011-11-07 0:00:00 3 1 2
2011-11-14 0:00:00 1 3 2
see sample at data exchange