Multiple rows selected from a date ranged row - sql-server-2005

Table example:
TableName:DownTime
Columns:
EventID - int,
ReasonID - int,
StartTime - DateTime,
EndTime - DateTime
I would like to calculate the daily downtime of each event over the last 30 days. However, an event could be down for multiple days, so the start time would could start 31 days prior and could end next week. So I need to return a row of data for that event 30 times. The event could also be 10 minutes long, so I need a row of data showing that.
So far I only get one row representing the entire event that occurs over 30 days instead of 30 rows.
MS SQL Server 2005 database
Thank you for any help.

You may want to create a date dimension table - a table that is a collection of days. The absolute simplest example for your purposes would have one column, date, and you would have one row for every consecutive day. Youu could then join this table to your downtime table:
from dim_date t join downtime d on t.date between convert(varchar(10), d.start_date, 120) and convert(varchar(10), d.end_date, 120)
This way if you have down time, you will get a row for every day containing down time.

before you try my code, you need to do a one time setup of a Numbers table. this will create a table names Numbers with a column Number having rows with values from 1 to 10,000:
SELECT TOP 10000 IDENTITY(int,1,1) AS Number
INTO Numbers
FROM sys.objects s1
CROSS JOIN sys.objects s2
ALTER TABLE Numbers ADD CONSTRAINT PK_Numbers PRIMARY KEY CLUSTERED (Number)
This Numbers table is joined to your existing table to "create" the rows you need, try this:
DECLARE #DownTime table (
EventID int,
ReasonID int,
StartTime DateTime,
EndTime DateTime)
INSERT INTO #DownTime VALUES (1,1,'9/10/2009 9:00am','9/10/2009 10:00am')
INSERT INTO #DownTime VALUES (2,1,'9/10/2009 1:00am','9/15/2009 1:00am')
INSERT INTO #DownTime VALUES (3,1,'9/10/2009' ,'9/11/2009')
SELECT
d.EventID,d.ReasonID,d.StartTime,d.EndTime
, DATEADD(day,Number-1,d.StartTime) AS SequenceDate
FROM #DownTime d
INNER JOIN Numbers n ON n.Number<=DATEDIFF(day,d.StartTime,d.EndTime)+1
OUTPUT:
EventID ReasonID StartTime EndTime SequenceDate
------- -------- ----------------------- ----------------------- -----------------------
1 1 2009-09-10 09:00:00.000 2009-09-10 10:00:00.000 2009-09-10 09:00:00.000
2 1 2009-09-10 01:00:00.000 2009-09-15 01:00:00.000 2009-09-10 01:00:00.000
2 1 2009-09-10 01:00:00.000 2009-09-15 01:00:00.000 2009-09-11 01:00:00.000
2 1 2009-09-10 01:00:00.000 2009-09-15 01:00:00.000 2009-09-12 01:00:00.000
2 1 2009-09-10 01:00:00.000 2009-09-15 01:00:00.000 2009-09-13 01:00:00.000
2 1 2009-09-10 01:00:00.000 2009-09-15 01:00:00.000 2009-09-14 01:00:00.000
2 1 2009-09-10 01:00:00.000 2009-09-15 01:00:00.000 2009-09-15 01:00:00.000
3 1 2009-09-10 00:00:00.000 2009-09-11 00:00:00.000 2009-09-10 00:00:00.000
3 1 2009-09-10 00:00:00.000 2009-09-11 00:00:00.000 2009-09-11 00:00:00.000
(9 row(s) affected)

Related

How to use next date column value to calculate delta for current column

I have a temp table
BusinessDate SSQ_CompScore
2011-01-05 00:00:00.000 41
2011-01-06 00:00:00.000 6
2011-01-07 00:00:00.000 1
2011-01-10 00:00:00.000 8
2011-01-11 00:00:00.000 48
2011-01-12 00:00:00.000 50
2011-01-13 00:00:00.000 59
I need to calculate delta for each current date.
I have prepared a solution but it doesn't work where date as not consecutive.
Can you please help?
select t1.businessdate, t1.ssq_compscore, (t2.ssq_compscore - t1.ssq_compscore) as delta
from #temp t1
left join #temp t2 on t1.businessdate = DATEADD(dd,1,t2.businessdate)
where t1.businessdate >='20180814'
Result set should be as
BusinessDate SSQ_CompScore Delta
2011-01-05 00:00:00.000 41 NULL
2011-01-06 00:00:00.000 6 35
2011-01-07 00:00:00.000 1 5
2011-01-10 00:00:00.000 8 7
2011-01-11 00:00:00.000 48 40
2011-01-12 00:00:00.000 50 2
2011-01-13 00:00:00.000 59 9
Not sure if this is the most efficient way but it works as far as I see
SELECT businessdate, SSQ_CompScore ,
SSQ_CompScore - (SELECT SSQ_CompScore
FROM temp
WHERE businessdate < t1.businessdate
ORDER BY businessdate DESC
LIMIT 1) as delta
FROM temp t1
ORDER BY businessdate ASC

Compare values for consecutive dates of same month

I have a table
ID Value Date
1 10 2017-10-02 02:50:04.480
2 20 2017-10-01 07:28:53.593
3 30 2017-09-30 23:59:59.000
4 40 2017-09-30 23:59:59.000
5 50 2017-09-30 02:36:07.520
I compare Value with previous date. But, I don't need compare result between first day in current month and last day in previous month. For this table, I don't need to compare result between 2017-10-01 07:28:53.593 and 2017-09-30 23:59:59.000 How it can be done?
Result table for this example:
ID Value Date Diff
1 10 2017-10-02 02:50:04.480 10
2 20 2017-10-01 07:28:53.593 NULL
3 30 2017-09-30 23:59:59.000 10
4 40 2017-09-29 23:59:59.000 10
5 50 2017-09-28 02:36:07.520 NULL
You can use this.
SELECT * ,
LEAD(Value) OVER( PARTITION BY DATEPART(YEAR,[Date]), DATEPART(MONTH,[Date]) ORDER BY ID ) - Value AS Diff
FROM MyTable
ORDER BY ID
you can use a query like below
select *,
diff=LEAD(Value) OVER( PARTITION BY Month(Date),Year(Date) ORDER BY Date desc)-Value
from t
order by id asc
see working demo

SQL Date Range Query - Table Comparison

I have two SQL Server tables containing the following information:
Table t_venues:
venue_id is unique
venue_id | start_date | end_date
1 | 01/01/2014 | 02/01/2014
2 | 05/01/2014 | 05/01/2014
3 | 09/01/2014 | 15/01/2014
4 | 20/01/2014 | 30/01/2014
Table t_venueuser:
venue_id is not unique
venue_id | start_date | end_date
1 | 02/01/2014 | 02/01/2014
2 | 05/01/2014 | 05/01/2014
3 | 09/01/2014 | 10/01/2014
4 | 23/01/2014 | 25/01/2014
From these two tables I need to find the dates that haven't been selected for each range, so the output would look like this:
venue_id | start_date | end_date
1 | 01/01/2014 | 01/01/2014
3 | 11/01/2014 | 15/01/2014
4 | 20/01/2014 | 22/01/2014
4 | 26/01/2014 | 30/01/2014
I can compare the two tables and get the date ranges from t_venues to appear in my query using 'except' but I can't get the query to produce the non-selected dates. Any help would be appreciated.
Calendar Table!
Another perfect candidate for a calendar table. If you can't be bothered to search for one, here's one I made earlier.
Setup Data
DECLARE #t_venues table (
venue_id int
, start_date date
, end_date date
);
INSERT INTO #t_venues (venue_id, start_date, end_date)
VALUES (1, '2014-01-01', '2014-01-02')
, (2, '2014-01-05', '2014-01-05')
, (3, '2014-01-09', '2014-01-15')
, (4, '2014-01-20', '2014-01-30')
;
DECLARE #t_venueuser table (
venue_id int
, start_date date
, end_date date
);
INSERT INTO #t_venueuser (venue_id, start_date, end_date)
VALUES (1, '2014-01-02', '2014-01-02')
, (2, '2014-01-05', '2014-01-05')
, (3, '2014-01-09', '2014-01-10')
, (4, '2014-01-23', '2014-01-25')
;
The Query
SELECT t_venues.venue_id
, calendar.the_date
, CASE WHEN t_venueuser.venue_id IS NULL THEN 1 ELSE 0 END As is_available
FROM dbo.calendar /* see: http://gvee.co.uk/files/sql/dbo.numbers%20&%20dbo.calendar.sql for an example */
INNER
JOIN #t_venues As t_venues
ON t_venues.start_date <= calendar.the_date
AND t_venues.end_date >= calendar.the_date
LEFT
JOIN #t_venueuser As t_venueuser
ON t_venueuser.venue_id = t_venues.venue_id
AND t_venueuser.start_date <= calendar.the_date
AND t_venueuser.end_date >= calendar.the_date
ORDER
BY t_venues.venue_id
, calendar.the_date
;
The Result
venue_id the_date is_available
----------- ----------------------- ------------
1 2014-01-01 00:00:00.000 1
1 2014-01-02 00:00:00.000 0
2 2014-01-05 00:00:00.000 0
3 2014-01-09 00:00:00.000 0
3 2014-01-10 00:00:00.000 0
3 2014-01-11 00:00:00.000 1
3 2014-01-12 00:00:00.000 1
3 2014-01-13 00:00:00.000 1
3 2014-01-14 00:00:00.000 1
3 2014-01-15 00:00:00.000 1
4 2014-01-20 00:00:00.000 1
4 2014-01-21 00:00:00.000 1
4 2014-01-22 00:00:00.000 1
4 2014-01-23 00:00:00.000 0
4 2014-01-24 00:00:00.000 0
4 2014-01-25 00:00:00.000 0
4 2014-01-26 00:00:00.000 1
4 2014-01-27 00:00:00.000 1
4 2014-01-28 00:00:00.000 1
4 2014-01-29 00:00:00.000 1
4 2014-01-30 00:00:00.000 1
(21 row(s) affected)
The Explanation
Our calendar tables contains an entry for every date.
We join our t_venues (as an aside, if you have the choice, lose the t_ prefix!) to return every day between our start_date and end_date. Example output for venue_id=4 for just this join:
venue_id the_date
----------- -----------------------
4 2014-01-20 00:00:00.000
4 2014-01-21 00:00:00.000
4 2014-01-22 00:00:00.000
4 2014-01-23 00:00:00.000
4 2014-01-24 00:00:00.000
4 2014-01-25 00:00:00.000
4 2014-01-26 00:00:00.000
4 2014-01-27 00:00:00.000
4 2014-01-28 00:00:00.000
4 2014-01-29 00:00:00.000
4 2014-01-30 00:00:00.000
(11 row(s) affected)
Now we have one row per day, we [outer] join our t_venueuser table. We join this in much the same manner as before, but with one added twist: we need to join based on the venue_id too!
Running this for venue_id=4 gives this result:
venue_id the_date t_venueuser_venue_id
----------- ----------------------- --------------------
4 2014-01-20 00:00:00.000 NULL
4 2014-01-21 00:00:00.000 NULL
4 2014-01-22 00:00:00.000 NULL
4 2014-01-23 00:00:00.000 4
4 2014-01-24 00:00:00.000 4
4 2014-01-25 00:00:00.000 4
4 2014-01-26 00:00:00.000 NULL
4 2014-01-27 00:00:00.000 NULL
4 2014-01-28 00:00:00.000 NULL
4 2014-01-29 00:00:00.000 NULL
4 2014-01-30 00:00:00.000 NULL
(11 row(s) affected)
See how we have a NULL value for rows where there is no t_venueuser record. Genius, no? ;-)
So in my first query I gave you a quick CASE statement that shows availability (1=available, 0=not available). This is for illustration only, but could be useful to you.
You can then either wrap the query up and then apply an extra filter on this calculated column or simply add a where clause in: WHERE t_venueuser.venue_id IS NULL and that will do the same trick.
This is a complete hack, but it gives the results you require, I've only tested it on the data you provided so there may well be gotchas with larger sets.
In general what you are looking at solving here is a variation of gaps and islands problem ,this is (briefly) a sequence where some items are missing. The missing items are referred as gaps and the existing items are referred as islands. If you would like to understand this issue in general check a few of the articles:
Simple talk article
blogs.MSDN article
SO answers tagged gaps-and-islands
Code:
;with dates as
(
SELECT vdates.venue_id,
vdates.vdate
FROM ( SELECT DATEADD(d,sv.number,v.start_date) vdate
, v.venue_id
FROM t_venues v
INNER JOIN master..spt_values sv
ON sv.type='P'
AND sv.number BETWEEN 0 AND datediff(d, v.start_date, v.end_date)) vdates
LEFT JOIN t_venueuser vu
ON vdates.vdate >= vu.start_date
AND vdates.vdate <= vu.end_date
AND vdates.venue_id = vu.venue_id
WHERE ISNULL(vu.venue_id,-1) = -1
)
SELECT venue_id, ISNULL([1],[2]) StartDate, [2] EndDate
FROM (SELECT venue_id, rDate, ROW_NUMBER() OVER (PARTITION BY venue_id, DateType ORDER BY rDate) AS rType, DateType as dType
FROM( SELECT d1.venue_id
,d1.vdate AS rDate
,'1' AS DateType
FROM dates AS d1
LEFT JOIN dates AS d0
ON DATEADD(d,-1,d1.vdate) = d0.vdate
LEFT JOIN dates AS d2
ON DATEADD(d,1,d1.vdate) = d2.vdate
WHERE CASE ISNULL(d2.vdate, '01 Jan 1753') WHEN '01 Jan 1753' THEN '2' ELSE '1' END = 1
AND ISNULL(d0.vdate, '01 Jan 1753') = '01 Jan 1753'
UNION
SELECT d1.venue_id
,ISNULL(d2.vdate,d1.vdate)
,'2'
FROM dates AS d1
LEFT JOIN dates AS d2
ON DATEADD(d,1,d1.vdate) = d2.vdate
WHERE CASE ISNULL(d2.vdate, '01 Jan 1753') WHEN '01 Jan 1753' THEN '2' ELSE '1' END = 2
) res
) src
PIVOT (MIN (rDate)
FOR dType IN
( [1], [2] )
) AS pvt
Results:
venue_id StartDate EndDate
1 2014-01-01 2014-01-01
3 2014-01-11 2014-01-15
4 2014-01-20 2014-01-22
4 2014-01-26 2014-01-30

Computation of period Start date

I have a table that hold the start date and the end date of a financial period.
CHARGE_PERIOD_ID START_DATE END_DATE
13 2013-03-31 00:00:00.000 2013-04-27 00:00:00.000
14 2013-04-28 00:00:00.000 2013-05-25 00:00:00.000
15 2013-05-26 00:00:00.000 2013-06-29 00:00:00.000
16 2013-06-30 00:00:00.000 2013-07-27 00:00:00.000
17 2013-07-28 00:00:00.000 2013-08-24 00:00:00.000
18 2013-08-25 00:00:00.000 2013-09-28 00:00:00.000
19 2013-09-29 00:00:00.000 2013-10-26 00:00:00.000
20 2013-10-27 00:00:00.000 2013-11-23 00:00:00.000
21 2013-11-24 00:00:00.000 2013-12-28 00:00:00.000
22 2013-12-29 00:00:00.000 2014-01-25 00:00:00.000
23 2014-01-26 00:00:00.000 2014-02-22 00:00:00.000
24 2014-02-23 00:00:00.000 2014-03-29 00:00:00.000
The user of a report wants the current financial year split into 12 periods and want to give to feed in 2 parameters into the report , a year and a period number which will go into my sql . So something like #year=2014 #period=1 will be recieved . I have to write some sql to go to this table and set a period start date of 31/03/2014 and a period end date of 27/04/2014.
So in pseudo code:
Look up period 1 for 2014 and return period start date of 31/03/2014 and period end date of 27/04/2014.
#PERIOD_START_DATE = select the the first period that starts in March for the given year . all financial period starts in March.
#PERIOD_END_DATE = select the corresponding END_DATE from the table .
The question is how to begin to code this or my design approach? Should I create a function that calcualtes this or should I do a CTE and add a column which will hold the period number in the way they want etc .
Thinking about it more I think I need a mapping table . So the real question is can I do this without a mapping table ?
DECLARE #Year INT
DECLARE #Period INT
SET #Year= 2013
SET #Period = 1
;WITH CTE AS
(
SELECT *, ROW_NUMBER() OVER (PARTITION BY
CASE WHEN MONTH([START_DATE])<3 THEN YEAR([START_DATE]) -1 ELSE YEAR([START_DATE]) END
ORDER BY
CASE WHEN MONTH([START_DATE])<3 THEN YEAR([START_DATE]) - 1 ELSE YEAR([START_DATE]) END
,CASE WHEN MONTH([START_DATE])<3 THEN MONTH([START_DATE]) + 12 ELSE MONTH([START_DATE]) END
) AS RN
FROM Periods
)
SELECT * FROM CTE
WHERE RN = #Period
AND CASE WHEN MONTH([START_DATE])<3 THEN YEAR([START_DATE]) -1 ELSE YEAR([START_DATE]) END = #Year
SQLFiddle DEMO

SQL server out of hour calculation

Out of hour calculation
An out of hour calendar defined as below,Here WeekNumber starts from 1 = Monday to 5 = Friday
CalendarId WeekNumber StartTime EndTime
600 1 1900-01-01 00:00 1900-01-01 08:00
600 1 1900-01-01 18:00 1900-01-01 23:59
600 2 1900-01-01 00:00 1900-01-01 08:00
600 2 1900-01-01 18:00 1900-01-01 23:59
600 3 1900-01-01 00:00 1900-01-01 08:00
600 3 1900-01-01 18:00 1900-01-01 23:59
600 4 1900-01-01 00:00 1900-01-01 08:00
600 4 1900-01-01 18:00 1900-01-01 23:59
600 5 1900-01-01 00:00 1900-01-01 08:00
600 5 1900-01-01 18:00 1900-01-01 23:59
I would like to apply this calendar to another table called events to find records falls in these day and times ?
Edit
The Structure of event table as follows
EventID StartDateTime TotalTimeInSec WeekNumber
1 2009-07-05 07:44 100 1
2 2009-07-05 08:40 200 1
3 2009-07-05 09:35 150 1
4 2009-07-05 10:37 200 1
5 2009-07-05 19:37 200 1
6 2009-07-05 20:37 200 1
The required output will be after appyling the calendar
EventID StartDateTime TotalTimeInSec WeekNumber
1 2009-07-05 07:44 100 1
5 2009-07-05 19:37 200 1
6 2009-07-05 20:37 200 1
Select a.WeekNumber,a.startDateTime,b.starttime,b.EndTime
from tblEvents a,(Select WeekNumber,Starttime,EndTime from tblMain) b
where a.startDateTime between b.starttime and b.EndTime
and a.WeekNumber = b.WeekNumber
Select E.*
From tblEvent E
Full outer Join tblMain M on E.WeekNumber = M.WeekNumber
Where E.StartDateTime Between M.StartTime and M.EndTime
I don't believe a full outer join is what you're asking for. And the other answer didn't seem to handle your method of storing time ranges as datetime values. The only tricky part seems to be handling that date math. You can find different ways to do that check, but I think this is one solution.
SELECT e.*
FROM
Events as e INNER JOIN Calendar as c
ON c.WeekNumber = e.WeekNumber
WHERE
/* CAST(CAST(e.StartDateTime AS TIME) AS DATETIME) -- later versions */
e.StartDateTime - DATEADD(dd, DATEDIFF(dd, 0, e.StartDateTime), 0)
BETWEEN c.StartTime and e.EndTime