I am using SQL SERVER. I have two tables table1 and table2. Both of them store time intervals, for simplicity just say both has two datetime2 column, column names are S1 and S2 for table 1, T1 and T2 for table2, for each row S1 is Greater than S2 , exactly for the table two. I want to calculate the value of intervals between S2 and S1(like a timeline) and minus it from overlap of T1 and T2 over S1 and S2. I tried this but can't go further than first part of calculation
DECLARE #x float
SET x=0
SELECT SUM(S1-S2)-x from table1
(set x =(SELECT (T1-T2) FROM table2
WHERE T1>=S1 and T2<=S2));
Example:
S2= 10/25/2012 ; S1= 11/30/2012;
assume that we have three rows in table 2
T2=10/20/2012 , T1=10/28/2012
T2=11/4/2012 , T1=11/8/2012
T2=11/22/2012 , T1=11/30/2012
what I want is to find total minutes between S1 and S2 except the minutes that overlapped with second table T1 and T2 intervals. My query works for the second row in second table when the whole interval between T1 and T2 is in the interval of S1 and S2.
This is somehow complicated hope this example helps
Query works fine but i can not calculate the overlap value with the query when one of the T1 or T2 are in the S1 and S2 interval. Should i run multiple queries? What are the parallels here?
I'm using SQL Server 2008 for this example.
This solution assumes that all intervals in table T do not overlap with each other.
The following articles explain interval algebra in detail and I think they are a very good read. They have nice diagrams as well.
Comparing date ranges
http://salman-w.blogspot.com.au/2012/06/sql-query-overlapping-date-ranges.html
http://www.ics.uci.edu/~alspaugh/cls/shr/allen.html
http://stewashton.wordpress.com/2014/03/11/sql-for-date-ranges-gaps-and-overlaps/
Create tables with sample data
I named the columns in a less confusing manner than in the original question. I've added few extra intervals that do not overlap to illustrate that proposed solution filters them out.
DECLARE #TableS TABLE (ID int IDENTITY(1,1), DateFromS date, DateToS date);
DECLARE #TableT TABLE (ID int IDENTITY(1,1), DateFromT date, DateToT date);
INSERT INTO #TableS (DateFromS, DateToS) VALUES ('2012-10-25', '2012-11-30');
INSERT INTO #TableS (DateFromS, DateToS) VALUES ('2015-10-25', '2015-11-30');
INSERT INTO #TableT (DateFromT, DateToT) VALUES ('2012-10-20', '2012-10-28');
INSERT INTO #TableT (DateFromT, DateToT) VALUES ('2012-11-04', '2012-11-08');
INSERT INTO #TableT (DateFromT, DateToT) VALUES ('2012-11-22', '2012-11-30');
INSERT INTO #TableT (DateFromT, DateToT) VALUES ('2010-11-22', '2010-11-30');
INSERT INTO #TableT (DateFromT, DateToT) VALUES ('2020-11-22', '2020-11-30');
Find overlapping intervals
I assume that we want to do these calculations for each row in the table S and for each row in table T. If this is not the case, you should join tables with some extra condition.
In this example I work only with days precision, not minutes, and I assume that start and end dates are inclusive, i.e. duration between 01/01/2000 and 01/01/2000 is one day. It should be fairly straightforward to extend this to minute precision.
SELECT *
,ISNULL(1+DATEDIFF(day, MaxDateFrom.DateFrom, MinDateTo.DateTo), 0) AS OverlappedDays
FROM
#TableS AS TS
LEFT JOIN #TableT AS TT ON TS.DateFromS <= TT.DateToT AND TS.DateToS >= TT.DateFromT
-- all periods in TS, which overlap with periods in TT
--(StartA <= EndB) and (EndA >= StartB)
CROSS APPLY
(
SELECT CASE WHEN TS.DateFromS > TT.DateFromT THEN TS.DateFromS ELSE TT.DateFromT END AS DateFrom
) AS MaxDateFrom
CROSS APPLY
(
SELECT CASE WHEN TS.DateToS < TT.DateToT THEN TS.DateToS ELSE TT.DateToT END AS DateTo
) AS MinDateTo
The condition in LEFT JOIN leaves only overlapping intervals. To calculate the duration of the overlapping interval I use two CROSS APPLYs. This is the result set of this intermediary query:
ID DateFromS DateToS ID DateFromT DateToT DateFrom DateTo OverlappedDays
1 2012-10-25 2012-11-30 1 2012-10-20 2012-10-28 2012-10-25 2012-10-28 4
1 2012-10-25 2012-11-30 2 2012-11-04 2012-11-08 2012-11-04 2012-11-08 5
1 2012-10-25 2012-11-30 3 2012-11-22 2012-11-30 2012-11-22 2012-11-30 9
2 2015-10-25 2015-11-30 NULL NULL NULL NULL NULL 0
Note, that the last row corresponds to the case when an interval in table S doesn't overlap with any intervals from table T.
Calculate durations
Now all we need is to sum up the duration of overlapping intervals T for each original row in table S and subtract it from the duration of the interval S.
SELECT
TS.ID
,TS.DateFromS
,TS.DateToS
,1+DATEDIFF(day, TS.DateFromS, TS.DateToS) AS DurationS
,ISNULL(SUM(1+DATEDIFF(day, MaxDateFrom.DateFrom, MinDateTo.DateTo)), 0) AS DurationOverlapped
,1+DATEDIFF(day, TS.DateFromS, TS.DateToS)
- ISNULL(SUM(1+DATEDIFF(day, MaxDateFrom.DateFrom, MinDateTo.DateTo)), 0) AS FinalDuration
FROM
#TableS AS TS
LEFT JOIN #TableT AS TT ON TS.DateFromS <= TT.DateToT AND TS.DateToS >= TT.DateFromT
CROSS APPLY
(
SELECT CASE WHEN TS.DateFromS > TT.DateFromT THEN TS.DateFromS ELSE TT.DateFromT END AS DateFrom
) AS MaxDateFrom
CROSS APPLY
(
SELECT CASE WHEN TS.DateToS < TT.DateToT THEN TS.DateToS ELSE TT.DateToT END AS DateTo
) AS MinDateTo
GROUP BY TS.ID, TS.DateFromS, TS.DateToS
This is the result set:
ID DateFromS DateToS DurationS DurationOverlapped FinalDuration
1 2012-10-25 2012-11-30 37 18 19
2 2015-10-25 2015-11-30 37 0 37
You are interested in the FinalDuration value, which is 19 for your example and 37 for the second interval that I added for this example.
You can add more intervals to the sample data to play with the queries and see how they work.
This solution assumes that all intervals in table T do not overlap with each other.
Related
Employee table has four columns. Employee, Start, End, Diff. Diff column is the duration and calculated as End - Start.
I want to find the conflict between the Start and End time range.
For instance, Employee A has three rows:
First rows Start time is 01:02 and end time is 01:05 but second row start time is 01:03 which is a conflict in the first row data.
Sample Data:
employee StartDate EndDate Start End Diff
A 04/08/2019 04/08/2019 01:02:00 01:05:00 3
A 04/08/2019 04/08/2019 01:03:00 01:08:00 5
A 04/08/2019 04/08/2019 01:014:00 01:21:00 7
B 04/08/2019 04/08/2019 02:00:00 02:17:00 17
I want to only select the specific start and end time for employee A that has an overlap in their start and end time and want to calculate total length of conflict in a new column using t-sql. i'm a newbie and need help. please anyone?
SELECT TOP (100) a.ccx_employeename AS employee
,CONVERT(Date,[a].[ccx_starttime]) AS [Start Date],CONVERT(Date,[a].[ccx_endtime]) AS [End Date], CONVERT(time (0), a.ccx_starttime) AS StartTime
, CONVERT(time (0), a.ccx_endtime) AS EndTime
, CONVERT (time(0), (a.ccx_endtime - a.ccx_starttime)) AS Duration
FROM ccp_sim_MSCRM.dbo.Filteredccx_Recorded_Service as a
where CONVERT(time (0), a.ccx_starttime) BETWEEN CONVERT(time (0), a.ccx_starttime) And CONVERT(time (0), a.ccx_endtime)
As first and second rows has conflict I want to show that two rows. As well as conflict duration is 2 minutes in this example. First row end time is 01:05 but second rows start time is 01:03 so conflict duration is 01:05 - 01:03 = 2 minutes
Desired Output
employee StartDate EndDate Start End Diff
A 04/08/2019 04/08/2019 01:02:00 01:05:00 3
A 04/08/2019 04/08/2019 01:03:00 01:08:00 5
duration of conflict : 2 mins
I would join the table over itself, though maybe not the most effective :
SELECT
e1.employee,
e1.Start as firstStart,
e1.End as firstEnd,
e2.Start as secondStart,
e2.End as secondEnd,
e1.End - e2.Start as conflictDuration
FROM
Employee as e1 inner join
Employee as e2 on (
e1.employee = e2.employee and
e2.Start < e1.End and
e2.End > e1.Start
)
There are a few parts to your question:
finding the conflicting rows
calculating the conflicting time
output in your desired format
The solution below only covers the first two parts and assumes a combined date and time field.
I have added some unique key to deduplicate the results and "sort" the rows for comparison. In the code below, it is "id".
declare #t table (id int identity,employee char(1), StartDateTime smalldatetime, EndDateTime smalldatetime, diff as DATEDIFF(minute,StartDateTime,EndDateTime))
insert into #t values('A','2019-04-08 01:02','2019-04-08 01:05')
insert into #t values('A','2019-04-08 01:03','2019-04-08 01:08')
insert into #t values('A','2019-04-08 01:14','2019-04-08 01:21')
insert into #t values('B','2019-04-08 02:00','2019-04-08 02:17')
SELECT T1.employee, T1.StartDateTime, T1.EndDateTime, T2.StartDateTime, T2.EndDateTime
, (T1.diff + T2.diff)
- DATEDIFF(minute, CASE WHEN T1.StartDateTime < T2.StartDateTime THEN T1.StartDateTime ELSE T2.StartDateTime END -- MIN(Start)
, CASE WHEN T1.EndDateTime > T2.EndDateTime THEN T1.EndDateTime ELSE T2.EndDateTime END) -- MAX(End)
AS "duration of conflict"
FROM #t AS T1
JOIN #t AS T2
ON T2.employee = T1.employee
AND T2.id > T1.id -- Each only once
AND T2.StartDateTime < T1.EndDateTime
AND T2.EndDateTime > T1.StartDateTime
This feels like the perfect place to use the LEAD/LAG functions to me. Combine that with some sub queries and IIF statements and you can calculate the results you're looking for.
Example:
DECLARE #Employee TABLE
(
Employee VARCHAR(1),
startDate DATE,
endDate DATE,
[start] TIME,
[end] TIME,
diff AS DATEDIFF(MINUTE,[start],[end])
)
INSERT INTO #Employee (Employee, startDate, endDate, start, [end])
VALUES
('A',CAST('2019-04-08' AS DATE),CAST('2019-04-08' AS DATE),'01:02:00','01:05:00'),
('A',CAST('2019-04-08' AS DATE),CAST('2019-04-08' AS DATE),'01:03:00','01:08:00'),
('A',CAST('2019-04-08' AS DATE),CAST('2019-04-08' AS DATE),'01:14:00','01:21:00'),
('B',CAST('2019-04-08' AS DATE),CAST('2019-04-08' AS DATE),'02:00:00','02:17:00')
SELECT
Employee.Employee,
Employee.startDate,
Employee.endDate,
Employee.start,
Employee.[end],
diff,
(IIF(ISNULL(lagConflict,0)>0,ISNULL(lagConflict,0),0)+IIF(ISNULL(Employee.leadConflict,0)>0,ISNULL(Employee.leadConflict,0),0)) AS conflict
FROM
(
SELECT
Employee,
startDate,
endDate,
start,
[end],
diff,
DATEDIFF
(
MINUTE,
[start],
LAG([end],1)
OVER
(
PARTITION BY
Employee,
startDate,
endDate
ORDER BY
[start],
[end]
)
) AS lagConflict,
DATEDIFF
(
MINUTE,
[end],
LEAD([start],1)
OVER
(
PARTITION BY
Employee,
startDate,
endDate
ORDER BY
[start],
[end]
)
)*-1 AS leadConflict
FROM
#Employee
) AS Employee
WHERE
Employee.leadConflict > 0
OR Employee.lagConflict > 0;
Microsoft SQL Docs: LAG]1
I'm designing a report that returns PurchaseOrder due in future week.
Query that I've added below returns PurchaseOrder due for a particular Commodity, AmountDue and its DeliveryDate.
Obviously it only returns PO_Dates that are in the table. What I want is to also include dates where no PO is expected, i.e. null for those cell.
To me one possibility is to LEFT JOIN the dataset with set of dates of future week on Date column, that will eventually make the result null where no Purchase Order is expected.
In Firebird I don't know how to select list of week long dates and then use it in the join.
SELECT
PURCHASE_ORDER_DET.COMMODITYID AS COM_ID,
PURCHASE_ORDER_DET.DELIVERYDATE + CAST ('29.12.1899' AS DATE) as DLV_DATE,
SUM(PURCHASE_ORDER_DET.REQQUANTITY) as DLV_DUE
FROM
PURCHASE_ORDER_DET
LEFT JOIN PURCHASE_ORDER_HDR on PURCHASE_ORDER_HDR.POH_ID =
PURCHASE_ORDER_DET.POH_ID
WHERE
PURCHASE_ORDER_DET.COMMODITYID = 1
AND PURCHASE_ORDER_HDR.STATUS in (0,1,2)
AND PURCHASE_ORDER_DET.DELIVERYDATE + CAST ('30.12.1899' AS TIMESTAMP) >= '3.01.2019'
AND PURCHASE_ORDER_DET.DELIVERYDATE + CAST ('30.12.1899' AS TIMESTAMP) <= '9.01.2019'
AND PURCHASE_ORDER_DET.DELETED is NULL
Group by
PURCHASE_ORDER_DET.COMMODITYID,
PURCHASE_ORDER_DET.DELIVERYDATE
DataSet
COM_ID DLV_DATE DLV_DUE
1 3.01.2019 50.000000
1 5.01.2019 10.000000
Expected
COM_ID DLV_DATE DLV_DUE
1 3.01.2019 50.000000
1 4.01.2019 null
1 5.01.2019 10.000000
1 6.01.2019 null
1 7.01.2019 null
1 8.01.2019 null
1 9.01.2019 null
Ignoring your odd use of datatypes*, there are several possible solutions:
Use a 'calendar' table that contains dates, and right join to that table (or left join from that table). The downside of course is having to populate this table (but that is a one-off cost).
Use a selectable stored procedure to generate a date range and join on that.
Generate the range in a recursive common table expression in the query itself
Option 1 is pretty self-explanatory.
Option 2 would look something like:
CREATE OR ALTER PROCEDURE date_range(startdate date, enddate date)
RETURNS (dateval date)
AS
BEGIN
dateval = startdate;
while (dateval <= enddate) do
BEGIN
suspend;
dateval = dateval + 1;
END
END
And then use this in your query like:
select date_range.dateval, ...
from date_range(date'2019-01-03', date'2019-01-09') -- use date_range(?, ?) for parameters
left join ...
on date_range.dateval = ...
Option 3 would look something like:
WITH RECURSIVE date_range AS (
SELECT date'2019-01-03' dateval -- start date, use cast(? as date) if you need a parameter
FROM rdb$database
UNION ALL
SELECT dateval + 1
FROM date_range
WHERE dateval < date'2019-01-09' -- end date use ? if you need a parameter
)
SELECT *
FROM date_range
LEFT JOIN ...
ON date_range.dateval = ...
Recursive common table expressions have a maximum recursion depth of 1024, which means that it isn't suitable if you need a span wider than 1024 days.
*: I'd suggest that you start using DATE instead of what looks like the number of days since 30-12-1899. That avoids having to do awkward calculations like you do now. If you do need those number of days, then you can for example use datediff(DAY FROM date'1899-12-30' TO somedatevalue) or somedatevalue - date'1899-12-30' to convert from date to that numeric value.
I have two columns (Created and ResolutionDate) in a table with the datetime values
I need to get the difference between the columns created and resolutiondate to get the number of days it took to be resolved from created date.
And also I need to get the result only with the working days or network days i.e., Monday to Friday (not the weekends and holidays).
For example, if I take created:2015-09-22 and resolutiondate: 2015-09-30, then the result should be 6 days, because two days are saturday and sunday between the created and resolutiondate I choose.
Please let me know how can I work it out with SQL.
For calculating the difference between two dates in working days, you can use the following function. Be aware that this will only calculate without weekends, and if you have holidays in the middle, it will calculate them as ordinary days.
public double GetBusinessDays(DateTime startD, DateTime endD)
{
double calcBusinessDays = 1 + ((endD - startD).TotalDays * 5 - (startD.DayOfWeek - endD.DayOfWeek) * 2) / 7;
if ((int)endD.DayOfWeek == 6) calcBusinessDays--;
if ((int)startD.DayOfWeek == 0) calcBusinessDays--;
return calcBusinessDays;
}
Perhaps something like this... The Cross Apply portion could be a UDF
Declare #YourTable table (ID int,Created datetime, ResolutionDate datetime)
Insert Into #YourTable values
(1,'2015-09-22 13:35:38','2015-09-30 17:37:09'),
(2,'2016-02-28 12:55:22','2016-02-29 12:55:44'),
(3,'2015-09-22 13:30:31','2015-09-30 17:37:09')
Select A.*
,B.WorkingDays
From #YourTable A
Cross Apply (
Select WorkingDays=count(*)
From (Select Top (DateDiff(DD,A.Created,A.ResolutionDate)+1) D=DateAdd(DD,Row_Number() over (Order By (Select NULL))-1,cast(cast(A.Created as date) as datetime)) From master..spt_values N1) D
Where D >= A.Created and D<= A.ResolutionDate
and DatePart(DW,D) not in (7,1)
and Cast(D as Date) Not In (Select Date From (Values
('2016-01-01','New Year''s Day'),
('2016-01-18','Martin Luther King, Jr,'),
('2016-02-15','Washington''s Birthday'),
('2016-03-25','Good Friday'),
('2016-05-30','Memorial Day'),
('2016-07-04','Independence Day'),
('2016-09-05','Labor Day'),
('2016-11-24','Thanksgiving'),
('2016-11-25','Black Friday'),
('2016-12-26','Christmas Day')
) as H (Date,Name))
) B
Returns
ID Created ResolutionDate WorkingDays
1 2015-09-22 13:35:38.000 2015-09-30 17:37:09.000 6
2 2016-02-28 12:55:22.000 2016-02-29 12:55:44.000 1
3 2015-09-22 13:30:31.000 2015-09-30 17:37:09.000 6
If you need company dates then have a table with all company work days
select ticket.ID, count(companyWorkDay.dt)
from ticket
left join companyWorkDay
on companyWorkDay.dt between ticket.created and ticket.resolution
group by ticket.ID
For the hassle of populating the table once you get a lot easier queries
You could write a query to enter the weekdays and then just remove the company holidays
I have data recording the StartDateTime and EndDateTime (both DATETIME2) of a process for all of the year 2013.
My task is to find the maximum amount of times the process was being ran at any specific time throughout the year.
I have wrote some code to check every minute/second how many processes were running at the specific time, but this takes a very long time and would be impossible to let it run for the whole year.
Here is the code (in this case check every minute for the date 25/10/2013)
CREATE TABLE dbo.#Hit
(
ID INT IDENTITY (1,1) PRIMARY KEY,
Moment DATETIME2,
COUNT INT
)
DECLARE #moment DATETIME2
SET #moment = '2013-10-24 00:00:00'
WHILE #moment < '2013-10-25'
BEGIN
INSERT INTO #Hit ( Moment, COUNT )
SELECT #moment, COUNT(*)
FROM dbo.tblProcessTimeLog
WHERE ProcessFK IN (25)
AND #moment BETWEEN StartDateTime AND EndDateTime
AND DelInd = 0
PRINT #moment
SET #moment = DATEADD(MINute,1,#moment)
END
SELECT * FROM #Hit
ORDER BY COUNT DESC
Can anyone think how i could get a similar result (I just need the maximum amount of processes being run at any given time), but for all year?
Thanks
DECLARE #d DATETIME = '20130101'; -- the first day of the year you care about
;WITH m(m) AS
( -- all the minutes in a day
SELECT TOP (1440) ROW_NUMBER() OVER (ORDER BY number) - 1
FROM master..spt_values
),
d(d) AS
( -- all the days in *that* year (accounts for leap years vs. hard-coding 365)
SELECT TOP (DATEDIFF(DAY, #d, DATEADD(YEAR, 1, #d))) DATEADD(DAY, number, #d)
FROM master..spt_values WHERE type = N'P' ORDER BY number
),
x AS
( -- all the minutes in *that* year
SELECT moment = DATEADD(MINUTE, m.m, d.d) FROM m CROSS JOIN d
)
SELECT TOP (1) WITH TIES -- in case more than one at the top
x.moment, [COUNT] = COUNT(l.ProcessFK)
FROM x
INNER JOIN dbo.tblProcessTimeLog AS l
ON x.moment >= l.StartDateTime
AND x.moment <= l.EndDateTime
WHERE l.ProcessFK = 25 AND l.DelInd = 0
GROUP BY x.moment
ORDER BY [COUNT] DESC;
See this post for why I don't think you should use BETWEEN for range queries, even in cases where it does semantically do what you want.
Create a table T whose rows represent some time segments.
This table could well be a temporary table (depending on your case).
Say:
row 1 - [from=00:00:00, to=00:00:01)
row 2 - [from=00:00:01, to=00:00:02)
row 3 - [from=00:00:02, to=00:00:03)
and so on.
Then just join from your main table
(tblProcessTimeLog, I think) to this table
based on the datetime values recorded in
tblProcessTimeLog.
A year has just about half million minutes
so it is not that many rows to store in T.
I recently pulled some code from SO trying to solve the 'island and gaps' problem, and the algorithm for that should help you solve your problem.
The idea is that you want to find the point in time that has the most started processes, much like figuring out the deepest nesting of parenthesis in an expression:
( ( ( ) ( ( ( (deepest here, 6)))))
This sql will produce this result for you (I included a temp table with sample data):
/*
CREATE TABLE #tblProcessTimeLog
(
StartDateTime DATETIME2,
EndDateTime DATETIME2
)
-- delete from #tblProcessTimeLog
INSERT INTO #tblProcessTimeLog (StartDateTime, EndDateTime)
Values ('1/1/2012', '1/6/2012'),
('1/2/2012', '1/6/2012'),
('1/3/2012', '1/6/2012'),
('1/4/2012', '1/6/2012'),
('1/5/2012', '1/7/2012'),
('1/6/2012', '1/8/2012'),
('1/6/2012', '1/10/2012'),
('1/6/2012', '1/11/2012'),
('1/10/2012', '1/12/2012'),
('1/15/2012', '1/16/2012')
;
*/
with cteProcessGroups (EventDate, GroupId) as
(
select EVENT_DATE, (E.START_ORDINAL - E.OVERALL_ORDINAL) GROUP_ID
FROM
(
select EVENT_DATE, EVENT_TYPE,
MAX(START_ORDINAL) OVER (ORDER BY EVENT_DATE, EVENT_TYPE ROWS UNBOUNDED PRECEDING) as START_ORDINAL,
ROW_NUMBER() OVER (ORDER BY EVENT_DATE, EVENT_TYPE) AS OVERALL_ORDINAL
from
(
Select StartDateTime AS EVENT_DATE, 1 as EVENT_TYPE, ROW_NUMBER() OVER (ORDER BY StartDateTime) as START_ORDINAL
from #tblProcessTimeLog
UNION ALL
select EndDateTime, 0 as EVENT_TYPE, NULL
FROM #tblProcessTimeLog
) RAWDATA
) E
)
select Max(EventDate) as EventDate, count(GroupId) as OpenProcesses
from cteProcessGroups
group by (GroupId)
order by COUNT(GroupId) desc
Results:
EventDate OpenProcesses
2012-01-05 00:00:00.0000000 5
2012-01-06 00:00:00.0000000 4
2012-01-15 00:00:00.0000000 2
2012-01-10 00:00:00.0000000 2
2012-01-08 00:00:00.0000000 1
2012-01-07 00:00:00.0000000 1
2012-01-11 00:00:00.0000000 1
2012-01-06 00:00:00.0000000 1
2012-01-06 00:00:00.0000000 1
2012-01-06 00:00:00.0000000 1
2012-01-16 00:00:00.0000000 1
Note that the 'in-between' rows don't give anything meaningful. Basically this output is only tuned to tell you when the most activity was. Looking at the other rows in the out put, there wasn't just 1 process running on 1/8 (there was actually 3). But the way this code works is that by grouping the processes that are concurrent together in a group, you can count the number of simultaneous processes. The date returned is when the max concurrent processes began. It doesn't tell you how long they were going on for, but you can solve that with an additional query. (once you know the date the most was ocurring, you can find out the specific process IDs by using a BETWEEN statement on the date.)
Hope this helps.
I'm using SQL Server 2008 R2 and trying to create a query that will show whether dates overlap.
I'm trying to calculate the number of days someone is covered under a certain criteria. Here is an example of the table...
CREATE TABLE mytable
(
CARDNBR varchar(10)
GPI char(14) ,
GPI_DESCRIPTION_10 varchar(50) ,
RX_DATE datetime ,
DAYS_SUPPLY int ,
END_DT datetime ,
METRIC_QUANTITY float
)
INSERT INTO mytable VALUES ('1234567890','27200040000315','Glyburide','01/30/2013','30','03/01/2013','60')
INSERT INTO mytable VALUES ('1234567890','27200040000315','Glyburide','03/04/2013','30','04/03/2013','60')
INSERT INTO mytable VALUES ('1234567890','27250050007520','Metformin','01/03/2013','30','02/02/2013','120')
INSERT INTO mytable VALUES ('1234567890','27250050007520','Metformin','02/27/2013','30','03/29/2013','120')
I want to be able to count the number of days that a person was covered from the first RX_DATE to the last END_DT, which in this example is 90 days (4/3/13 - 1/3/13).
That part is done, but this is where I'm getting into trouble.
Between row 1 and row 2, there was a 3 day period where there were no drugs being taken. Between rows 3 and 4 there was a 25 day period. However, during that 25 day period, row 1 covered that gap. So the end number I need to show is 3 for the gap between rows 1 and 2.
Any help would be greatly appreciated.
Thanks.
There might be a better approach, but you could create a lookup of days, join to it and select the distinct days that join, that will get you the total count of days covered for all lines:
CREATE TABLE #lkp_Calendar (Dt DATE)
GO
SET NOCOUNT ON
DECLARE #intFlag INT
SET #intFlag = 1
WHILE (#intFlag <=500)
BEGIN
--Loop through this:
INSERT INTO #lkp_Calendar
SELECT DATEADD(day,#intFlag,'20120101')
SET #intFlag = #intFlag + 1
END
GO
--Days Covered
SELECT CARDNBR, COUNT(DISTINCT b.Dt)CT
FROM #mytable a
JOIN #lkp_Calendar b
ON b.Dt BETWEEN a.RX_DATE AND a.END_DT
GROUP BY CARDNBR
--Total Days
SELECT CARDNBR, DATEDIFF(DAY,MIN(RX_DATE),MAX(END_DT))+1 'Total_Days'
FROM #mytable
GROUP BY CARDNBR
--Combined
SELECT covered.CARDNBR, covered.CT 'Days Covered', total.Total_Days 'Total Days', total.Total_Days - covered.CT 'Days Gap'
FROM (SELECT CARDNBR, COUNT(DISTINCT b.Dt)CT
FROM #mytable a
JOIN #lkp_Calendar b
ON b.Dt BETWEEN a.RX_DATE AND a.END_DT
GROUP BY CARDNBR
)covered
JOIN (SELECT CARDNBR, DATEDIFF(DAY,MIN(RX_DATE),MAX(END_DT))+1 'Total_Days'
FROM #mytable
GROUP BY CARDNBR
)total
ON covered.CARDNBR = total.CARDNBR
You said 90 days, but I believe you should have 91. Date diff from Mon-Wed is only 2, but that's 3 days covered. But you can decide if coverage begins on the rx date or the day after.