SQL Server - Split a date range by a given date - sql-server

I have a table that stores an Id and an effective period indicating when it is active
PortfolioId StartDate EndDate
1 2018-01-01 00:00:00.000 2018-05-31 00:00:00.000
2 2017-01-01 00:00:00.000 2018-05-31 00:00:00.000
I have another table that stores a component related to the above Id and that too has an effective period. Table 2 can have more that on entry for any given entry in table 1.
PortfolioComponentId PortfolioId SplitDate
1 1 2018-02-28 00:00:00.000
2 1 2018-03-31 00:00:00.000
3 2 2017-03-31 00:00:00.000
4 2 2017-09-20 00:00:00.000
5 2 2018-01-15 00:00:00.000
I have a period where I am running the query for
i.e
StartDate : 30-JUN-2017
End date : 15-MAY-2018
I have looking for a result like the below, where data in table 1 is split based on the data from table 2
PortfolioId StartDate EndDate
1 2018-01-01 00:00:00.000 2018-02-28 00:00:00.000
1 2018-03-01 00:00:00.000 2018-03-31 00:00:00.000 - Starts from End date + 1 from the prev row
1 2018-04-01 00:00:00.000 2018-05-15 00:00:00.000
2 2017-06-30 00:00:00.000 2017-09-20 00:00:00.000 - Starts from Seach date [Portfolio component Id 3 ignored as it falls outside of search date range]
2 2017-09-21 00:00:00.000 2018-01-15 00:00:00.000
2 2018-01-16 00:00:00.000 2018-05-15 00:00:00.000 - Ends by seach end date
Data setup - In case it helps
DECLARE #SearchStartDate DATETIME = '30-JUN-2017'
DECLARE #SearchEndDate DATETIME = '15-MAY-2018'
DECLARE #Portfolio TABLE
(
PortfolioId INT PRIMARY KEY IDENTITY(1,1),
StartDate DATETIME,
EndDate DATETIME
)
INSERT INTO #Portfolio
SELECT '01-JAN-2018', '31-MAY-2018'
INSERT INTO #Portfolio
SELECT '01-JAN-2017', '31-MAY-2018'
DECLARE #PortfolioComponents TABLE
(
PortfolioComponentId INT PRIMARY KEY IDENTITY(1,1),
PortfolioId INT,
SplitDate DATETIME
)
INSERT INTO #PortfolioComponents
SELECT 1, '28-FEB-2018'
INSERT INTO #PortfolioComponents
SELECT 1, '31-MAR-2018'
INSERT INTO #PortfolioComponents
SELECT 2, '31-MAR-2017'
INSERT INTO #PortfolioComponents
SELECT 2, '20-SEP-2017'
INSERT INTO #PortfolioComponents
SELECT 2, '15-JAN-2018'
SELECT * from #Portfolio
SELECT * from #PortfolioComponents

I believe I have got a result close to what I want, though not
the most ideal approach
the best approach from a performance perspective
DECLARE #Temp TABLE (BenchmarkDate Datetime, ComponentType int, PortfolioId INT)
INSERT INTO #Temp
SELECT StartDate , 1, PortfolioId FROM #Portfolio
UNION
SELECT EndDate , 2, PortfolioId FROM #Portfolio
INSERT INTO #Temp
SELECT SplitDate , 2, PortfolioId FROM #PortfolioComponents
UNION
SELECT SplitDate + 1 , 1, PortfolioId FROM #PortfolioComponents
DECLARE #Results TABLE
(
Id INT IDENTITY(1,1),
StartDate DATETIME,
EndDate DATETIME,
PortfolioId INT
)
INSERT INTO #Results
SELECT rset1.BenchmarkDate [Startdate],
( SELECT MIN(rset2.BenchmarkDate)
FROM #Temp rset2
WHERE rset2.ComponentType = 2
AND rset2.BenchmarkDate > rset1.BenchmarkDate
AND rset1.PortfolioId = rset2.PortfolioId) [Enddate],
rset1.PortfolioId
FROM #Temp rset1
WHERE rset1.ComponentType = 1
ORDER BY rset1.PortfolioId, rset1.BenchmarkDate
SELECT
CASE WHEN (#SearchStartDate BETWEEN StartDate AND EndDate ) THEN #SearchStartDate ELSE StartDate END StartDate,
CASE WHEN (#SearchEndDate BETWEEN StartDate AND EndDate) THEN #SearchEndDate ELSE EndDate END EndDate,
PortfolioId ,
(
SELECT PortfolioComponentId
FROM #PortfolioComponents pc
WHERE
(pc.PortfolioID = r.PortfolioId AND
(DATEADD(d, 1, pc.SplitDate) = r.StartDate ))
) PortfolioComponentId
FROM #Results r
WHERE
(#SearchStartDate < StartDate AND #SearchStartDate < EndDate AND #SearchEndDate > EndDate)
OR
(#SearchEndDate BETWEEN StartDate AND EndDate)
OR
(#SearchStartDate BETWEEN StartDate AND EndDate )

Related

Split date range to multiple rows using SQL

I have a table:
startdate enddate other_columns
1956-05-06 00:00:00.000 1960-04-05 00:00:00.000 myvalues
I need a query that will return the results as:
startdate enddate other_columns
1956-05-06 00:00:00.000 1956-12-31 00:00:00.000 myvalues
1957-01-01 00:00:00.000 1957-12-31 00:00:00.000 myvalues
1958-01-01 00:00:00.000 1958-12-31 00:00:00.000 myvalues
1959-01-01 00:00:00.000 1959-12-31 00:00:00.000 myvalues
1960-01-01 00:00:00.000 1960-04-05 00:00:00.000 myvalues
Basically a query that will explode the rows into yearly results. I need the start and end dates to be retained.
CREATE TABLE #InputTABLE
(
startdate DATETIME,
enddate DATETIME,
other_columns varchar(20)
)
INSERT INTO #InputTABLE VALUES('1956-05-06','1960-04-05','myvalues');
SELECT * FROM #InputTABLE
Output:
startdate enddate other_columns
1956-05-06 00:00:00.000 1960-04-05 00:00:00.000 myvalues
Query:
CREATE TABLE #OutputTABLE
(
startdate DATETIME,
enddate DATETIME,
other_columns varchar(20)
)
DECLARE #cnt int
DECLARE #startDate datetime
DECLARE #endDate datetime
DECLARE #incr int
DECLARE #tempDate datetime
SET #startDate=(Select startdate from #InputTABLE)
SET #endDate=(Select enddate from #InputTABLE)
SET #cnt=DATEDIFF(yy,#startDate,#endDate)
SET #incr=0
SET #tempDate=DATEADD(yy,#incr,Cast(#startDate As datetime))
WHILE #cnt>=0
BEGIN
IF #cnt = 0
BEGIN
INSERT INTO #OutputTABLE VALUES(#tempDate,#endDate,'myvalues');
END
ELSE
BEGIN
insert into #OutputTABLE values(#tempDate,DATEADD(yy, DATEDIFF(yy,0,#tempDate)+1, -1),'myvalues');
END
SET #tempDate=DATEADD(yy,#incr+1,DATEADD(yy,DATEDIFF(yy,0,#startDate),0))
SET #cnt=#cnt-1
SET #incr=#incr+1
END
Result : SELECT * FROM #OutputTABLE;
startdate enddate other_columns
1956-05-06 00:00:00.000 1956-12-31 00:00:00.000 myvalues
1957-01-01 00:00:00.000 1957-12-31 00:00:00.000 myvalues
1958-01-01 00:00:00.000 1958-12-31 00:00:00.000 myvalues
1959-01-01 00:00:00.000 1959-12-31 00:00:00.000 myvalues
1960-01-01 00:00:00.000 1960-04-05 00:00:00.000 myvalues
The query is quite simple
SELECT CASE WHEN yrStart<it.startdate THEN it.startdate ELSE yrStart END AS startdate,
CASE WHEN yrEnd>it.enddate THEN it.enddate ELSE yrEnd END AS enddate,
other_columns
FROM #InputTABLE it
CROSS APPLY
(SELECT datefromparts(yr, 1, 1) yrStart, datefromparts(yr, 12, 31) yrEnd
FROM dbo.yearTable
WHERE yr >= datepart(year, it.startdate) AND yr <= datepart(year, it.enddate)
)years;
all you need is a yearTable with numbers 1..9999 (aka Tally table). You should not have numbers outside of this range in this table, otherwise you would meet some nasty conversion errors.

How to find missing dates by ID

I need help modifying a script to find the missing RevenuePeriod by ID within a given time frame. I have found a similar script that finds the missing date for a given ID but I don't know how to modify the script where it will give the missing dates per ID.
create table #WorksheetHistory (WorksheetID [int] IDENTITY(1,1) ,ID varchar(6), RevenuePeriod datetime)
insert into #WorksheetHistory (ID,RevenuePeriod)
SELECT '000001','2015-06-01 00:00:00.00' Union All
SELECT '000001','2015-07-01 00:00:00.00' Union All
SELECT '000001','2015-11-01 00:00:00.00' Union All
SELECT '000001','2015-12-01 00:00:00.00' Union All
SELECT '000002','2015-06-01 00:00:00.00' Union All
SELECT '000002','2015-12-01 00:00:00.00'
DECLARE #EndDate datetime
DECLARE #StartDate datetime
SET #StartDate = '2015-06-01 00:00:00.00'
SET #EndDate = '2015-12-01 00:00:00.00'
;WITH Dates as
(
SELECT #StartDate AS dt
UNION ALL
SELECT DATEADD(month, 1, dt) as dt
FROM Dates
WHERE dt < (select dateadd(month,-1,#EndDate) enddate)
)
select Month(dt) as dtMonth, Year(dt) dtYear
from Dates d left outer join #WorksheetHistory w
on dateadd(month, datediff(month,0,d.dt),0) = dateadd(month, datediff(month,0,w.RevenuePeriod),0) Where RevenuePeriod is null
Drop Table #WorksheetHistory
The current output returns the following output. I realize in the script it doesnt have an ID returned, but even if I do, it would return null value as the script indicates return null dates. I don't know how to put the associated ID in it too.
dt
2015-08-01 00:00:00.000
2015-09-01 00:00:00.000
2015-10-01 00:00:00.000
My desired result would be return the Missing ID's with the respective missing dates.
ID dt
000001 2015-08-01 00:00:00.00
000001 2015-09-01 00:00:00.00
000001 2015-10-01 00:00:00.00
000002 2015-07-01 00:00:00.00
000002 2015-08-01 00:00:00.00
000002 2015-09-01 00:00:00.00
000002 2015-10-01 00:00:00.00
000002 2015-11-01 00:00:00.00
Use EXCEPT:
WITH Dates as
(
SELECT #StartDate AS dt
UNION ALL
SELECT DATEADD(month, 1, dt) as dt
FROM Dates
WHERE dt < DATEADD(m, -1, #EndDate)
)
-- all the possible combinations
SELECT w.ID, d.dt
FROM Dates d
CROSS JOIN (
SELECT ID
FROM #WorksheetHistory
GROUP BY ID
) w
EXCEPT
-- the combinations you actually have
SELECT w.ID, d.dt
FROM Dates d
JOIN #WorksheetHistory w
ON d.dt = w.RevenuePeriod;

How do i display the end date from StartDate in sql server?

I am using SQL SERVER 2008. I have a table called Cut-Off table. This table only shows the StartDate of cut-off for every month. So i am creating a temp table that will store StartDate, EndDate, Month and Year. But i don't know how to get the EndDate with datetime.
TABLE
----------------------------------------------------------
StartDate EndDate Month Year
----------------------------------------------------------
2014-12-15 00:00:00 NULL 12 2014
2015-01-26 00:00:00 NULL 1 2015
2015-02-26 00:00:00 NULL 2 2015
---------------------------------------------------------
EXPECTED OUTPUT
--------------------------------------------------------------------
StartDate EndDate Month Year
------------------------------------------------------------------
2014-12-15 00:00:00 2015-01-25 23:59:99 12 2014
2015-01-26 00:00:00 2015-02-25 23:59:99 1 2015
2015-02-26 00:00:00 2015-03-25 23:59:99 2 2015
------------------------------------------------------------------
How i get the EndDate??
Was unable to get 99 seconds in '23:59:99'
Try this:
CREATE TABLE #temp(StartDate datetime, EndDate datetime, Month int, Year int)
INSERT #temp values
('2014-12-15 00:00:00', NULL, 12, 2014),
('2015-01-26 00:00:00', NULL, 1, 2015),
('2015-02-26 00:00:00', NULL, 2, 2015)
UPDATE t
SET EndDate = DateAdd(m, DateDiff(m, 0, StartDate), '1900-02-25T23:59:59')
FROM #temp t
SELECT * FROM #temp
Result:
StartDate EndDate Month Year
2014-12-15 00:00:00.000 2015-01-25 23:59:59.000 12 2014
2015-01-26 00:00:00.000 2015-02-25 23:59:59.000 1 2015
2015-02-26 00:00:00.000 2015-03-25 23:59:59.000 2 2015
Edit:
If you want to calculate the EndDate from next StartDate, here is your UPDATE statement:
UPDATE t
SET EndDate = (SELECT top 1 DateAdd(s, -1, StartDate) FROM #temp
WHERE t.StartDate < StartDate
ORDER BY startdate)
FROM #temp t
Result:
StartDate EndDate Month Year
2014-12-15 00:00:00.000 2015-01-25 23:59:59.000 12 2014
2015-01-26 00:00:00.000 2015-02-25 23:59:59.000 1 2015
2015-02-26 00:00:00.000 null 2 2015
SAMPLE TABLE
CREATE TABLE #TEMP(DATES DATETIME,DATES DATETIME,[MONTH] INT,[YEAR] INT)
INSERT INTO #TEMP
SELECT '2014-12-15 00:00:00', NULL, 12, 2014
UNION ALL
SELECT '2015-01-26 00:00:00', NULL, 1, 2015
UNION ALL
SELECT '2015-02-26 00:00:00', NULL, 2, 2015
QUERY
;WITH CTE AS
(
SELECT ROW_NUMBER() OVER(ORDER BY STARTDATE)RNO, STARTDATE
FROM #TEMP
)
,CTE2 AS
(
SELECT C1.STARTDATE,
ISNULL(DATEADD(S,-1,C2.STARTDATE),DATEADD(S,-1,DATEADD(MONTH,1,C1.STARTDATE))) ENDDATE
FROM CTE C1
LEFT JOIN CTE C2 ON C1.RNO=C2.RNO-1
)
UPDATE #TEMP
SET EndDate = CTE2.ENDDATE
FROM #TEMP T1
JOIN CTE2 ON T1.STARTDATE=CTE2.STARTDATE
RESULT
You can also do it with OUTER APPLY
select
c.StartDate
DATEADD(SECOND,-1,e.StartDate) EndDate
DATEPART(MONTH,StartDate) [Month],
DATEPART(YEAR,StartDate) [Year]
from [Cut-Off] c
outer apply (
select TOP 1
e.StartDate
from [Cut-Off] e
where
e.StartDate > c.StartDate
order by
e.StartDate
) e

TSQL for Repeating rows within table

I have single table with below fields.
id name startdate enddate
1 u1 2013-01-15 00:00:00.000 2013-01-17 00:00:00.000
2 u2 2013-01-22 00:00:00.000 2013-01-23 00:00:00.000
3 u3 2013-01-23 00:00:00.000 2013-01-23 00:00:00.000
Now, I want multiple rows depends on start and end dates. So as per above rows.. It returns with three rows for first record.. date 15 to 17 which returns 3 rows (3 days).
I am bit confused for query. Is there any better way or any sample to achieve?
Thanks.
You could use a CTE to solve that:
DECLARE #Id int
SELECT #Id = 1
;
WITH Multiple AS
(
SELECT 1 Sequence, Id, Name, StartDate, EndDate
FROM ( VALUES
(1, 'u1', '2013-01-15', '2013-01-17'),
(2, 'u2', '2013-01-22', '2013-01-23'),
(3, 'u3', '2013-01-23', '2013-01-23')
) AS Sample(Id, Name, StartDate, EndDate)
WHERE Id = #Id
UNION ALL
SELECT Sequence + 1, Id, Name, StartDate, EndDate
FROM Multiple
WHERE Id = #Id AND DATEADD(d, Sequence, StartDate) <= EndDate
)
SELECT *
FROM Multiple
If you have a 'Dates' table with a 'Date' column in there, just join your table to the 'Dates' on 'Dates.Date BETWEEN startdate AND enddate'.
I am not pretty sure but below is what I have tried..
SELECT c1.*
FROM master..spt_values p
INNER JOIN tempQuery c1
ON RIGHT(CAST(c1.stdate AS DATE),2) <= (CASE WHEN p.number = 0 THEN 1 ELSE p.number END)
WHERE TYPE='p'
AND p.number BETWEEN RIGHT(CAST(c1.stdate AS DATE),2) AND RIGHT(CAST(c1.endate AS DATE),2)

sql query to solve the following

I have to following table in sql server:
date | status
2009-01-01 12:00:00 OK
2009-01-01 12:03:00 FAILED
2009-01-01 12:04:00 OK
2009-01-01 12:06:20 OK
2009-01-01 12:07:35 FAILED
2009-01-01 12:07:40 FAILED
2009-01-01 12:20:40 FAILED
2009-01-01 12:25:40 OK
I need the following: starting 2009-01-01 12:00:00, every 10 minute from this date i need to see the number of OK and FAILED.
something like:
INTERVAL FAILED OK
2009-01-01 12:00:00-2009-01-01 12:15:00 1 2
2009-01-01 12:15:01-2009-01-01 12:30:00 0 1
etc..
what is the best way to do this in sql?
Ok first of all ..
You mention 10 minutes and the provide an example with 15 minutes.. Additionally you sample data should return different results than what you posted..
solution using Pivot
Declare #datetimestart datetime
Declare #interval int
Set #datetimestart = '2009-01-01 12:00:00'
Set #interval = 15
Select
*
From
(
Select
DateAdd( Minute,Floor(DateDiff(Minute,#datetimestart,[date])/#interval)*#interval
,#datetimestart),
DateAdd( Minute,#interval + Floor(DateDiff(Minute,#datetimestart,[date])/#interval)*#interval
,#datetimestart)
, status
From dtest
) As W([from],[to], status)
Pivot (Count(status) For status In ([ok],[failed])) p
this will return
From To Ok Failed
2009-01-01 12:00:00.000 2009-01-01 12:15:00.000 3 3
2009-01-01 12:15:00.000 2009-01-01 12:30:00.000 1 0
Update after comments
This version will include time intervals that do not have values in the database..
We will need to create a temporary table on the fly ..
Declare #datetimestart datetime, #datetimeend datetime, #datetimecurrent datetime
Declare #interval int
Set #datetimestart = '2009-01-01 12:00:00'
Set #interval = 10
Set #datetimeend = (Select max([date]) from dtest)
SET #datetimecurrent = #datetimestart
declare #temp as table ([from] datetime,[to] datetime)
while #datetimecurrent < #datetimeend
BEGIN
insert into #temp select (#datetimecurrent), dateAdd( minute, #interval, #datetimecurrent)
set #datetimecurrent = dateAdd( minute, #interval, #datetimecurrent)
END
Select
*
From
(
Select
[from],[to], status
From #temp t left join dtest d on d.[date] between t.[from] and t.[to]
) As W([from],[to], status)
Pivot (Count(status) For status In ([ok],[failed])) p
Using a 10 minute interval now, to show a period without values, returns..
From To Ok Failed
2009-01-01 12:00:00.000 2009-01-01 12:10:00.000 3 3
2009-01-01 12:10:00.000 2009-01-01 12:20:00.000 0 0
2009-01-01 12:20:00.000 2009-01-01 12:30:00.000 1 0
There might be an easier way to do it but this works:
--CREATE TABLE temptest
--(
-- date1 DATETIME,
-- stat nvarchar(10)
--)
--INSERT INTO temptest
--VALUES
--('2009-01-01 12:00:00','OK'),
--('2009-01-01 12:03:00','FAILED'),
--('2009-01-01 12:04:00','OK'),
--('2009-01-01 12:06:20','OK'),
--('2009-01-01 12:07:35','FAILED'),
--('2009-01-01 12:07:40','FAILED'),
--('2009-01-01 12:20:40','FAILED'),
--('2009-01-01 12:25:40','OK')
SELECT
stat,
COUNT(1),
YEAR(date1),
MONTH(date1),
DAY(date1),
DATEPART(hh,date1),
ROUND(DATEPART(MINUTE,date1)/10,0)
FROM temptest
GROUP BY stat, YEAR(date1), MONTH(date1), DAY(date1), DATEPART(hh,date1), ROUND(DATEPART(MINUTE,date1)/10,0)
Because I don't know your table name, something like this SHOULD work.
DECLARE #startTime DATETIME
DECLARE #endTime DATETIME
SELECT #startTime = '1/1/2010 00:00:00'
SELECT #endTime = GETDATE()
SELECT
cast(#startTime as varchar) + ' - ' + cast(#endTime as varchar) as Interval,
(select count(1) from [table] where status = 'FAILED') as FAILED,
(Select count(1) from [table where status = 'OK') as OK
FROM
[table]
WHERE
date between #startTime and #endTime
This is using a recursive CTE.
declare #startdate datetime
declare #enddate datetime
declare #interval int
set #startdate = '2009-01-01 12:00:00'
set #enddate = '2009-01-02 12:00:00'
set #interval = 15
;with intervals ( i, d ) AS
(
select 1, #startdate
union all
select i+1, DATEADD(MINUTE, (#interval*i), #startdate) from intervals where i < 100
)
select d as 'From', DATEADD(MINUTE, (#interval-1), d) as 'To',
(select COUNT(*) from yourTable where thedate between d and DATEADD(MINUTE, (#interval-1), d) and thestatus = 'FAILED') as 'FAILED',
(select COUNT(*) from yourTable where thedate between d and DATEADD(MINUTE, (#interval-1), d) and thestatus = 'OK') as 'OK'
from intervals
option (MAXRECURSION 100)
The output looks like this:
From To FAILED OK
----------------------- ----------------------- ----------- -----------
2009-01-01 12:00:00.000 2009-01-01 12:14:00.000 3 3
2009-01-01 12:15:00.000 2009-01-01 12:29:00.000 1 1
2009-01-01 12:30:00.000 2009-01-01 12:44:00.000 0 0
2009-01-01 12:45:00.000 2009-01-01 12:59:00.000 0 0
2009-01-01 13:00:00.000 2009-01-01 13:14:00.000 0 0
2009-01-01 13:15:00.000 2009-01-01 13:29:00.000 0 0
2009-01-01 13:30:00.000 2009-01-01 13:44:00.000 0 0
Please note in your data you have the same number of failed and ok in the time slots.
Another option...
CREATE TABLE #results ( IntervalStart DATETIME, IntervalEnd DATETIME, FailedCount INT, OKCount INT );
DECLARE #EndPoint DATETIME
DECLARE #CurrentPoint DATETIME
DECLARE #PeriodEnd DATETIME
SET #CurrentPoint = '2009-01-01 12:00:00'
SET #EndPoint = '2009-03-01 12:00:00' -- choose any end point, could be today: GETDATE()
WHILE #CurrentPoint < #EndPoint
BEGIN
SET #PeriodEnd = DATEADD(mi, 10, #CurrentPoint)
INSERT INTO #results
SELECT #CurrentPoint, #PeriodEnd,
(SELECT COUNT(Status) FROM StatusSource WHERE StatusPoint BETWEEN #CurrentPoint AND #PeriodEnd AND Status = 'FAILED'),
(SELECT COUNT(Status) FROM StatusSource WHERE StatusPoint BETWEEN #CurrentPoint AND #PeriodEnd AND Status = 'OK')
SET #CurrentPoint = #PeriodEnd
END
SELECT
CAST(#IntervalStart AS VARCHAR(20)) + ' - ' + cast(#IntervalEnd AS VARCHAR(20)) as Interval,
FailedCount AS FAILED,
OKCount AS OK
FROM
#results
DROP TABLE #results
Here's the tally table version.
Set up some dummy data:
/*
CREATE TABLE MyTable
(
MyDate DATETIME,
Status varchar(10)
)
INSERT INTO Mytable VALUES ('2009-01-01 12:00:00','OK')
INSERT INTO Mytable VALUES ('2009-01-01 12:03:00','FAILED')
INSERT INTO Mytable VALUES ('2009-01-01 12:04:00','OK')
INSERT INTO Mytable VALUES ('2009-01-01 12:06:20','OK')
INSERT INTO Mytable VALUES ('2009-01-01 12:07:35','FAILED')
INSERT INTO Mytable VALUES ('2009-01-01 12:07:40','FAILED')
INSERT INTO Mytable VALUES ('2009-01-01 12:20:40','FAILED')
INSERT INTO Mytable VALUES ('2009-01-01 12:25:40','OK')
*/
Set up values and paramters. I hard-coded everything for 10 minute intervals, but this too could be a paramter.
DECLARE
#StartAt datetime
,#Through datetime
SET #StartAt = 'Jan 1, 2009'
SET #Through = getdate() -- or whenever
And the query. This lists rows only when there is data to list; make it an inner join to also list "time slots" without activity.
;WITH
-- Itzik Ben-Gan's tally table routine
Pass0 as (select 1 as C union all select 1), --2 rows
Pass1 as (select 1 as C from Pass0 as A, Pass0 as B),--4 rows
Pass2 as (select 1 as C from Pass1 as A, Pass1 as B),--16 rows
Pass3 as (select 1 as C from Pass2 as A, Pass2 as B),--256 rows
Pass4 as (select 1 as C from Pass3 as A, Pass3 as B),--65536 rows
Pass5 as (select 1 as C from Pass4 as A, Pass4 as B),--4,294,967,296 rows
Tally as (select row_number() over(order by C) as Number from Pass5)
(...look up discussions on "tally tables" or "table of numbers" for the what-and-why's behind this...)
select
xx.FromTime
,sum(case when mt.Status = 'OK' then 1 else 0 end) HowManyOk
,sum(case when mt.Status = 'Failed' then 1 else 0 end) HowManyFailed
from (select
dateadd(mi, (Number-1) * 10, #StartAt) FromTime
,dateadd(mi, Number * 10, #StartAt) ThruTime
from Tally where Number <= datediff(mi, #StartAt, #Through) /10) xx
inner join MyTable mt
on mt.MyDate >= xx.FromTime and mt.MyDate < xx.ThruTime
group by xx.FromTime
So my question is: of all the methods presented, which scales better as data volume increases? I hope somebody tests this.

Resources