SQL Query getting the date between expiration date and effective date from 2 different tables - sql

I having this problem with query these two table do not have any link between and I am trying to combine. Table 1 have effective and expiration date in which have to be tied to table 2 of PollDate. PollDate must not lies in between the effective and expiration date.
Table 1
ClientID EffectiveDate ExpirationDate
1 2009-04-01 00:00:00.000 2009-12-18 00:00:00.000
1 2010-02-12 00:00:00.000 2010-03-05 00:00:00.000
1 2010-05-18 00:00:00.000 NULL
1 2009-12-21 00:00:00.000 2010-02-08 00:00:00.000
1 2010-12-19 00:00:00.000 2009-12-20 00:00:00.000
Table 2
ClientID PollDate
1 2009-12-20 00:00:00.000
1 2009-12-19 00:00:00.000
1 2010-02-12 00:00:00.000
1 2010-02-27 00:00:00.000
1 2010-05-19 00:00:00.000
1 2010-05-29 00:00:00.000
1 2010-05-30 00:00:00.000
1 2010-05-31 00:00:00.000
1 2010-06-05 00:00:00.000
1 2010-06-25 00:00:00.000
1 2010-06-27 00:00:00.000
1 2010-07-02 00:00:00.000
1 2010-08-04 00:00:00.000
1 2010-08-20 00:00:00.000
Result
ClientID inValidDate
1 2009-12-20 00:00:00.000
1 2009-12-19 00:00:00.000

The following is a slight variation of #AJP's result - just accounting for NULL values in ExpirationDate:
CREATE TABLE #Table1
(
[ClientID] INT,
[EffectiveDate] DATETIME,
[ExpirationDate] DATETIME
)
INSERT INTO #Table1
(
[ClientID],
[EffectiveDate],
[ExpirationDate]
)
SELECT 1, '2009-04-01', '2009-12-18' UNION
SELECT 1, '2010-02-12', '2010-03-05' UNION
SELECT 1, '2010-05-18', NULL UNION
SELECT 1, '2009-12-21', '2010-02-08' UNION
SELECT 1, '2010-12-19', '2009-12-20'
CREATE TABLE #Table2
(
[ClientID] INT,
[PollDate] DATETIME
)
INSERT INTO #Table2
(
[ClientID],
[PollDate]
)
SELECT 1, '2009-12-20' UNION
SELECT 1, '2009-12-19' UNION
SELECT 1, '2010-02-12' UNION
SELECT 1, '2010-02-27' UNION
SELECT 1, '2010-05-19' UNION
SELECT 1, '2010-05-29' UNION
SELECT 1, '2010-05-30' UNION
SELECT 1, '2010-05-31' UNION
SELECT 1, '2010-06-05' UNION
SELECT 1, '2010-06-25' UNION
SELECT 1, '2010-06-27' UNION
SELECT 1, '2010-07-02' UNION
SELECT 1, '2010-08-04' UNION
SELECT 1, '2010-08-20'
SELECT
t2.[ClientID],
t2.[PollDate] AS 'inValidDate'
FROM
#Table1 AS t1
JOIN
#Table2 AS t2
ON
(t2.[PollDate] < t1.[EffectiveDate]
OR t2.[PollDate] > ISNULL(t1.[ExpirationDate], '9999-12-31'))
AND t1.ClientID = t2.ClientID -- Not clear from your question if this is necessary

can u try:
select t2.ClientID, t2.PoolDate
from Table1 t1, Table2 t2
where t2.PollDate between t1.EffectiveDate and t1.ExpirationDate
because u didn't put in the join condition, each row of table1 will match with each row of table2

not sure about syntext but u want to do something like this.
select clientID, polDate as 'inValidDate'
FROM Table1 t1
INNER JOIN Table2 t2
ON t2.PolDate not in between t1.EffectiveDate and t1.ExpirationDate
EDIT:
Assuming if expiration date is null means policy will never expire.
select clientID, polDate as 'inValidDate'
FROM Table1 t1
INNER JOIN Table2 t2
ON t2.PolDate not in between t1.EffectiveDate and ISNULL(t1.ExpirationDate, '2999-01-01')

Related

SQL Start Date and End Date Matching

I have one table that contains customer id and start date and one table that contains customer id and end date.
table A
customer_id
start_date
1
2022-01-01
1
2022-04-01
1
2022-07-01
2
2022-01-15
2
2022-03-25
3
2022-04-01
3
2022-08-01
4
2022-09-01
table B
customer_id
end_date
1
2022-01-25
1
2022-05-03
2
2022-03-24
2
2022-03-29
3
2022-04-15
Is there a way that I can get an output that looks like below?
desired output
customer_id
start_date
end_date
1
2022-01-01
2022-01-25
1
2022-04-01
2022-05-03
1
2022-07-01
2
2022-01-15
2022-03-24
2
2022-03-25
2022-03-29
3
2022-04-01
2022-04-15
3
2022-08-01
4
2022-09-01
As per your desire result please check the below query you can change it as per your table name and requirements.
DECLARE #table1 TABLE(
[customer_id] INT,
[start_date] DATE
)
DECLARE #table2 TABLE(
[customer_id] INT,
[end_date] DATE
)
INSERT INTO #table1 VALUES
(1,'2022-01-01'),
(1,'2022-04-01'),
(1,'2022-07-01'),
(2,'2022-01-15'),
(2,'2022-03-25'),
(3,'2022-04-01'),
(3,'2022-08-01'),
(4,'2022-09-01')
INSERT INTO #table2 VALUES
(1,'2022-01-25'),
(1,'2022-05-03'),
(2,'2022-03-24'),
(2,'2022-03-29'),
(3,'2022-04-15')
SELECT [Table1].[customer_id],[Table1].[start_date],[Table2].[end_date] FROM (
SELECT *, ROW_NUMBER() OVER (ORDER BY [start_date]) row_num FROM #table1
) AS [Table1]
LEFT JOIN (
SELECT t2.*, ROW_NUMBER() OVER (ORDER BY [end_date] ) row_num FROM #table2 t2
) AS [Table2]
ON [Table2].[customer_id] = [Table1].[customer_id]
AND [Table1].[row_num] = [Table2].[row_num]
ORDER BY [Table1].[customer_id]
Output
This hint is using TSQL in SQL Server.
select A.customer_id, A.start_date, B.end_date
from
(select X.costumer_id, X.start_date, ROW_NUMBER() over (order by X.start_date) as ORDEM from TableA X) A
left outer join (select X.customer_id, X.end_date, ROW_NUMBER() over (order by X.end_date ) as ORDEM from TableB X) B on A.customer_id = B.customer_id and A.ORDEM = B.ORDEM
order by A.customer_id, A.start_date
Hope it helps.
Here is how it's done in Vertica 12 - now that we have INTERPOLATE NEXT VALUE for the event series join :
\pset null (null)
WITH
tba(customer_id,start_date) AS (
SELECT 1,DATE '2022-01-01'
UNION ALL SELECT 1,DATE '2022-04-01'
UNION ALL SELECT 1,DATE '2022-07-01'
UNION ALL SELECT 2,DATE '2022-01-15'
UNION ALL SELECT 2,DATE '2022-03-25'
UNION ALL SELECT 3,DATE '2022-04-01'
UNION ALL SELECT 3,DATE '2022-08-01'
UNION ALL SELECT 4,DATE '2022-09-01'
)
,
tbb(customer_id,end_date) AS (
SELECT 1,DATE '2022-01-25'
UNION ALL SELECT 1,DATE '2022-05-03'
UNION ALL SELECT 2,DATE '2022-03-24'
UNION ALL SELECT 2,DATE '2022-03-29'
UNION ALL SELECT 3,DATE '2022-04-15'
)
SELECT
tba.customer_id
, start_date
, end_date
FROM tba
LEFT JOIN tbb
ON tba.customer_id = tbb.customer_id
AND end_date INTERPOLATE NEXT VALUE start_date
ORDER BY 1,2
;
-- out Null display is "(null)".
-- out customer_id | start_date | end_date
-- out -------------+------------+------------
-- out 1 | 2022-01-01 | 2022-01-25
-- out 1 | 2022-04-01 | 2022-05-03
-- out 1 | 2022-07-01 | (null)
-- out 2 | 2022-01-15 | 2022-03-24
-- out 2 | 2022-03-25 | 2022-03-29
-- out 3 | 2022-04-01 | 2022-04-15
-- out 3 | 2022-08-01 | (null)
-- out 4 | 2022-09-01 | (null)

filter result rows to have alternating state in time series

Using standard TSQL the result i need has rows with an alternating binary BIT state in DATETIME order, the source rows are in DATETIME order but have redundant states, meaning there was no change in state from the previous row.
Source Rows:
State Date
1 2019-06-01 09:00:00.000
0 2019-06-02 00:00:00.000
1 2019-06-02 15:00:00.000
1 2019-06-03 00:00:00.000 - redundant
1 2019-06-03 09:00:00.000 - redundant
1 2019-06-04 09:00:00.000 - redundant
0 2019-06-04 09:15:00.000
1 2019-06-04 09:45:00.000
1 2019-06-05 09:00:00.000 - redundant
0 2019-06-06 09:00:00.000
1 2019-06-07 09:00:00.000
0 2019-06-07 12:15:00.000
In pseudo SQL, the query to solve my problem would look like this:
SELECT State, Date
FROM Source_Rows
WHERE State <> (State from previous row)
ORDER BY Date
Expected Result:
State Date
1 2019-06-01 09:00:00.000
0 2019-06-02 00:00:00.000
1 2019-06-02 15:00:00.000
0 2019-06-04 09:15:00.000
1 2019-06-04 09:45:00.000
0 2019-06-06 09:00:00.000
1 2019-06-07 09:00:00.000
0 2019-06-07 12:15:00.000
^ state column alternates 1-0-1-0-1-0-1
You can use LAG function to compare current state with previous:
WITH cte AS (
SELECT *, LAG(State) OVER (ORDER BY Date) AS PrevState
FROM t
)
SELECT *
FROM cte
WHERE State <> PrevState OR PrevState IS NULL
SELECT
ROW_NUMBER() OVER (ORDER BY x.[Date]) AS RId,
x.*
INTO #TblStateDate
FROM
(
SELECT 1 AS [State],'2019-06-01 09:00:00.000' AS [Date] UNION ALL
SELECT 0 AS [State],'2019-06-02 00:00:00.000' AS [Date] UNION ALL
SELECT 1 AS [State],'2019-06-02 15:00:00.000' AS [Date] UNION ALL
SELECT 1 AS [State],'2019-06-03 00:00:00.000' AS [Date] UNION ALL
SELECT 1 AS [State],'2019-06-03 09:00:00.000' AS [Date] UNION ALL
SELECT 1 AS [State],'2019-06-04 09:00:00.000' AS [Date] UNION ALL
SELECT 0 AS [State],'2019-06-04 09:15:00.000' AS [Date] UNION ALL
SELECT 0 AS [State],'2019-06-04 09:30:00.000' AS [Date] UNION ALL --redundant extra added
SELECT 1 AS [State],'2019-06-04 09:45:00.000' AS [Date] UNION ALL
SELECT 1 AS [State],'2019-06-05 09:00:00.000' AS [Date] UNION ALL
SELECT 1 AS [State],'2019-06-05 09:15:00.000' AS [Date] UNION ALL --redundant extra added
SELECT 1 AS [State],'2019-06-05 09:15:15.000' AS [Date] UNION ALL --redundant extra added
SELECT 0 AS [State],'2019-06-06 09:00:00.000' AS [Date] UNION ALL
SELECT 1 AS [State],'2019-06-07 09:00:00.000' AS [Date] UNION ALL
SELECT 0 AS [State],'2019-06-07 12:15:00.000' AS [Date] UNION ALL --redundant extra added
SELECT 0 AS [State],'2019-06-07 12:30:00.000' AS [Date] UNION ALL --redundant extra added
SELECT 0 AS [State],'2019-06-07 12:45:00.000' AS [Date] --redundant extra added
) x
SELECT
t1.[State],
t1.[Date]
FROM #TblStateDate t1
LEFT JOIN #TblStateDate t2 ON t2.RId = t1.RId - 1
WHERE (t1.[State] <> t2.[State] OR t2.[State] IS NULL)
DROP TABLE #TblStateDate;
Explanation: This query can be use from SQL Server 2005 onward versions by finding previous state value using record identity column.
Note: I have added extra redundant records in select union all script. Still you will get your expected result.
Ref. URL for search previous value: https://blog.sqlauthority.com/2013/09/25/sql-server-how-to-access-the-previous-row-and-next-row-value-in-select-statement-part-4/

SQL Server select missing Dates in result set

I have one table containing Employee Daily Attendance punchtime in space separated form.
EmployeePunch
EmpID EmpName Date Time
1 ABC 2014-12-01 10:00 18:00
1 ABC 2014-12-02 09:50 17:50
1 ABC 2014-12-04 09:30 17:30
1 ABC 2014-12-07 10:00 18:00
1 ABC 2014-12-08 09:50 17:50
1 ABC 2014-12-10 09:30 17:30
Now I want to write a query for following output
EmpID EmpName Date Time
1 ABC 2014-12-01 10:00 18:00
1 ABC 2014-12-02 09:50 17:50
1 ABC 2014-12-03 ABSENT
1 ABC 2014-12-04 09:30 17:30
1 ABC 2014-12-05 ABSENT
1 ABC 2014-12-06 ABSENT
1 ABC 2014-12-07 10:00 18:00
1 ABC 2014-12-08 09:50 17:50
1 ABC 2014-12-09 ABSENT
1 ABC 2014-12-10 09:30 17:30
First define CTE to generate missing records:
WITH dates AS (
SELECT DISTINCT EmpId, EmpName, '2014-12-01' AS Date, 'ABSENT' AS Time
FROM EmployeePunch
UNION
SELECT EmpId, EmpName, DATEADD(DAY, 1, Date), 'ABSENT'
FROM dates
WHERE Date < DATEADD(DAY, -1, DATEADD(MONTH, 1, '2014-12-01')))
SELECT * FROM dates
In the next step replace the last line with:
SELECT * FROM EmployeePunch
UNION ALL
SELECT d.* FROM dates d
LEFT JOIN EmployeePunch e
ON e.EmpId = d.EmpId AND e.Date = d.Date
WHERE e.Time IS NULL
The missing rows are the outerjoined ones.
Without CTE:
select ep1.EmpId, ep1.EmpName, a.Date, ISNULL(ep2.Time, 'ABSENT') as Time
from (
select DATEADD(day, a.a + (10 * b.a) + (100 * c.a), CAST('2014-12-01' /*begin date*/ AS DATE)) as Date
from (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as a
cross join (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as b
cross join (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as c
) a cross apply (select distinct EmpId, EmpName from EmployeePunch) ep1 --on a.Date = f.Date
left join EmployeePunch ep2 on ep2.Date = a.Date and ep2.EmpId = ep1.EmpId
where a.Date <= '2014-12-10' and ep1.EmpId is not null
Be aware about the maximal allowed range - 1000 days, but it can be extended if necessary

Determine contiguous date intervals

I have the following table structure:
id int -- more like a group id, not unique in the table
AddedOn datetime -- when the record was added
For a specific id there is at most one record each day. I have to write a query that returns contiguous (at day level) date intervals for each id.
The expected result structure is:
id int
StartDate datetime
EndDate datetime
Note that the time part of AddedOn is available but it is not important here.
To make it clearer, here is some input data:
with data as
(
select * from
(
values
(0, getdate()), --dummy record used to infer column types
(1, '20150101'),
(1, '20150102'),
(1, '20150104'),
(1, '20150105'),
(1, '20150106'),
(2, '20150101'),
(2, '20150102'),
(2, '20150103'),
(2, '20150104'),
(2, '20150106'),
(2, '20150107'),
(3, '20150101'),
(3, '20150103'),
(3, '20150105'),
(3, '20150106'),
(3, '20150108'),
(3, '20150109'),
(3, '20150110')
) as d(id, AddedOn)
where id > 0 -- exclude dummy record
)
select * from data
And the expected result:
id StartDate EndDate
1 2015-01-01 2015-01-02
1 2015-01-04 2015-01-06
2 2015-01-01 2015-01-04
2 2015-01-06 2015-01-07
3 2015-01-01 2015-01-01
3 2015-01-03 2015-01-03
3 2015-01-05 2015-01-06
3 2015-01-08 2015-01-10
Although it looks like a common problem I couldn't find a similar enough question. Also I'm getting closer to a solution and I will post it when (and if) it works but I feel that there should be a more elegant one.
Here's answer without any fancy joining, but simply using group by and row_number, which is not only simple but also more efficient.
WITH CTE_dayOfYear
AS
(
SELECT id,
AddedOn,
DATEDIFF(DAY,'20000101',AddedOn) dyID,
ROW_NUMBER() OVER (ORDER BY ID,AddedOn) row_num
FROM data
)
SELECT ID,
MIN(AddedOn) StartDate,
MAX(AddedOn) EndDate,
dyID-row_num AS groupID
FROM CTE_dayOfYear
GROUP BY ID,dyID - row_num
ORDER BY ID,2,3
The logic is that the dyID is based on the date so there are gaps while row_num has no gaps. So every time there is a gap in dyID, then it changes the difference between row_num and dyID. Then I simply use that difference as my groupID.
In Sql Server 2008 it is a little bit pain without LEAD and LAG functions:
WITH data
AS ( SELECT * ,
ROW_NUMBER() OVER ( ORDER BY id, AddedOn ) AS rn
FROM ( VALUES ( 0, GETDATE()), --dummy record used to infer column types
( 1, '20150101'), ( 1, '20150102'), ( 1, '20150104'),
( 1, '20150105'), ( 1, '20150106'), ( 2, '20150101'),
( 2, '20150102'), ( 2, '20150103'), ( 2, '20150104'),
( 2, '20150106'), ( 2, '20150107'), ( 3, '20150101'),
( 3, '20150103'), ( 3, '20150105'), ( 3, '20150106'),
( 3, '20150108'), ( 3, '20150109'), ( 3, '20150110') )
AS d ( id, AddedOn )
WHERE id > 0 -- exclude dummy record
),
diff
AS ( SELECT d1.* ,
CASE WHEN ISNULL(DATEDIFF(dd, d2.AddedOn, d1.AddedOn),
1) = 1 THEN 0
ELSE 1
END AS diff
FROM data d1
LEFT JOIN data d2 ON d1.id = d2.id
AND d1.rn = d2.rn + 1
),
parts
AS ( SELECT * ,
( SELECT SUM(diff)
FROM diff d2
WHERE d2.rn <= d1.rn
) AS p
FROM diff d1
)
SELECT id ,
MIN(AddedOn) AS StartDate ,
MAX(AddedOn) AS EndDate
FROM parts
GROUP BY id ,
p
Output:
id StartDate EndDate
1 2015-01-01 00:00:00.000 2015-01-02 00:00:00.000
1 2015-01-04 00:00:00.000 2015-01-06 00:00:00.000
2 2015-01-01 00:00:00.000 2015-01-04 00:00:00.000
2 2015-01-06 00:00:00.000 2015-01-07 00:00:00.000
3 2015-01-01 00:00:00.000 2015-01-01 00:00:00.000
3 2015-01-03 00:00:00.000 2015-01-03 00:00:00.000
3 2015-01-05 00:00:00.000 2015-01-06 00:00:00.000
3 2015-01-08 00:00:00.000 2015-01-10 00:00:00.000
Walkthrough:
diff
This CTE returns data:
1 2015-01-01 00:00:00.000 1 0
1 2015-01-02 00:00:00.000 2 0
1 2015-01-04 00:00:00.000 3 1
1 2015-01-05 00:00:00.000 4 0
1 2015-01-06 00:00:00.000 5 0
You are joining same table on itself to get the previous row. Then you calculate difference in days between current row and previous row and if the result is 1 day then pick 0 else pick 1.
parts
This CTE selects result from previous step and sums up the new column(it is a cumulative sum. sum of all values of new column from starting till current row), so you are getting partitions to group by:
1 2015-01-01 00:00:00.000 1 0 0
1 2015-01-02 00:00:00.000 2 0 0
1 2015-01-04 00:00:00.000 3 1 1
1 2015-01-05 00:00:00.000 4 0 1
1 2015-01-06 00:00:00.000 5 0 1
2 2015-01-01 00:00:00.000 6 0 1
2 2015-01-02 00:00:00.000 7 0 1
2 2015-01-03 00:00:00.000 8 0 1
2 2015-01-04 00:00:00.000 9 0 1
2 2015-01-06 00:00:00.000 10 1 2
2 2015-01-07 00:00:00.000 11 0 2
3 2015-01-01 00:00:00.000 12 0 2
3 2015-01-03 00:00:00.000 13 1 3
The last step is just a grouping by ID and new column and picking min and max values for dates.
I took the "Islands Solution #3 from SQL MVP Deep Dives" solution from https://www.simple-talk.com/sql/t-sql-programming/the-sql-of-gaps-and-islands-in-sequences/ and applied to your test data:
with
data as
(
select * from
(
values
(0, getdate()), --dummy record used to infer column types
(1, '20150101'),
(1, '20150102'),
(1, '20150104'),
(1, '20150105'),
(1, '20150106'),
(2, '20150101'),
(2, '20150102'),
(2, '20150103'),
(2, '20150104'),
(2, '20150106'),
(2, '20150107'),
(3, '20150101'),
(3, '20150103'),
(3, '20150105'),
(3, '20150106'),
(3, '20150108'),
(3, '20150109'),
(3, '20150110')
) as d(id, AddedOn)
where id > 0 -- exclude dummy record
)
,CTE_Seq
AS
(
SELECT
ID
,SeqNo
,SeqNo - ROW_NUMBER() OVER (PARTITION BY ID ORDER BY SeqNo) AS rn
FROM
data
CROSS APPLY
(
SELECT DATEDIFF(day, '20150101', AddedOn) AS SeqNo
) AS CA
)
SELECT
ID
,DATEADD(day, MIN(SeqNo), '20150101') AS StartDate
,DATEADD(day, MAX(SeqNo), '20150101') AS EndDate
FROM CTE_Seq
GROUP BY ID, rn
ORDER BY ID, StartDate;
Result set
ID StartDate EndDate
1 2015-01-01 00:00:00.000 2015-01-02 00:00:00.000
1 2015-01-04 00:00:00.000 2015-01-06 00:00:00.000
2 2015-01-01 00:00:00.000 2015-01-04 00:00:00.000
2 2015-01-06 00:00:00.000 2015-01-07 00:00:00.000
3 2015-01-01 00:00:00.000 2015-01-01 00:00:00.000
3 2015-01-03 00:00:00.000 2015-01-03 00:00:00.000
3 2015-01-05 00:00:00.000 2015-01-06 00:00:00.000
3 2015-01-08 00:00:00.000 2015-01-10 00:00:00.000
I'd recommend you to examine the intermediate results of CTE_Seq to understand how it actually works. Just put
select * from CTE_Seq
instead of the final SELECT ... GROUP BY .... You'll get this result set:
ID SeqNo rn
1 0 -1
1 1 -1
1 3 0
1 4 0
1 5 0
2 0 -1
2 1 -1
2 2 -1
2 3 -1
2 5 0
2 6 0
3 0 -1
3 2 0
3 4 1
3 5 1
3 7 2
3 8 2
3 9 2
Each date is converted into a sequence number by DATEDIFF(day, '20150101', AddedOn). ROW_NUMBER() generates a set of sequential numbers without gaps, so when these numbers are subtracted from a sequence with gaps the difference jumps/changes. The difference stays the same until the next gap, so in the final SELECT GROUP BY ID, rn brings all rows from the same island together.
Here is a simple solution that does not use analytics. I tend not to use analytics because I work with many different DBMSs and many don't (yet) have them emplemented and even those who do have different syntaxes. I just have the habit of writing generic code whenever possible.
with
Data( ID, AddedOn )as(
select 1, convert( date, '20150101' ) union all
select 1, '20150102' union all
select 1, '20150104' union all
select 1, '20150105' union all
select 1, '20150106' union all
select 2, '20150101' union all
select 2, '20150102' union all
select 2, '20150103' union all
select 2, '20150104' union all
select 2, '20150106' union all
select 2, '20150107' union all
select 3, '20150101' union all
select 3, '20150103' union all
select 3, '20150105' union all
select 3, '20150106' union all
select 3, '20150108' union all
select 3, '20150109' union all
select 3, '20150110'
)
select d.ID, d.AddedOn StartDate, IsNull( d1.AddedOn, '99991231' ) EndDate
from Data d
left join Data d1
on d1.ID = d.ID
and d1.AddedOn =(
select Min( AddedOn )
from data
where ID = d.ID
and AddedOn > d.AddedOn );
In your situation I assume that ID and AddedOn form a composite PK and so are indexed. Thus, the query will run impressively fast even on very large tables.
Also, I used the outer join because it seemed like the last AddedOn date of each ID should be seen in the StartDate column. Instead of NULL I used a common MaxDate value. The NULL could work just as well as a "this is the latest StartDate row" flag.
Here is the output for ID=1:
ID StartDate EndDate
----------- ---------- ----------
1 2015-01-01 2015-01-02
1 2015-01-02 2015-01-04
1 2015-01-04 2015-01-05
1 2015-01-05 2015-01-06
1 2015-01-06 9999-12-31
I'd like to post my own solution too because it's yet another approach:
with data as
(
...
),
temp as
(
select d.id
,d.AddedOn
,dprev.AddedOn as PrevAddedOn
,dnext.AddedOn as NextAddedOn
FROM data d
left JOIN
data dprev on dprev.id = d.id
and dprev.AddedOn = dateadd(d, -1, d.AddedOn)
left JOIN
data dnext on dnext.id = d.id
and dnext.AddedOn = dateadd(d, 1, d.AddedOn)
),
starts AS
(
select id
,AddedOn
from temp
where PrevAddedOn is NULL
),
ends as
(
select id
,AddedOn
from temp
where NextAddedon is NULL
)
SELECT s.id as id
,s.AddedOn as StartDate
,(select min(e.AddedOn) from ends e where e.id = s.id and e.AddedOn >= s.AddedOn) as EndDate
from starts s

sql query to find absent list with date in sql server

I have two attendance tables.One isemployeelist and another is attendence_info.Employeelist contain Emp_Id and Emp_name. Attendance_info is Emp_Id, Date.As below:
Emp_ID Date
----------- -----------------------
1 2014-12-11 00:00:00.000
2 2014-12-11 00:00:00.000
4 2014-12-11 00:00:00.000
5 2014-12-11 00:00:00.000
2 2014-12-10 00:00:00.000
4 2014-12-10 00:00:00.000
5 2014-12-10 00:00:00.000
1 2014-12-09 00:00:00.000
2 2014-12-09 00:00:00.000
3 2014-12-09 00:00:00.000
Here each date some id are absent. I want to find out all absent list with date.Please help to find it by Sql server query. My desired output should be as below:
absentId Date
3 2014-12-11 00:00:00.000
1 2014-12-10 00:00:00.000
3 2014-12-10 00:00:00.000
4 2014-12-09 00:00:00.000
5 2014-12-09 00:00:00.000
You can do this by generating a list of all employees and dates and then removing the ones where the employee is present. This is basically a cross join and left join:
select el.emp_id, d.date
from (select distinct date from Attendance_info) d cross join
employeelist el left join
Attendance_info ai
on ai.date = d.date and ai.emp_id = el.emp_id
where ai.emp_id is null;
select e.Emp_ID as absentId,a.Date as date
from employeelist e
join attendence_info a
on e.Emp_ID=a.Emp_ID
order by a.Date
DECLARE #StartDate DATETIME = '2014-12-09'
DECLARE #EndDate DATETIME = '2014-12-11'
;WITH MyCte ([Date])
AS
(
SELECT [Date] = #StartDate
UNION ALL
SELECT DATEADD(DAY, 1, [Date]) FROM MyCte WHERE [Date] < #EndDate
)
,EmployeeList ([EmpID], [Date])
AS
(
SELECT 1, '2014-12-11 00:00:00.000' UNION
SELECT 2, '2014-12-11 00:00:00.000' UNION
SELECT 4, '2014-12-11 00:00:00.000' UNION
SELECT 5, '2014-12-11 00:00:00.000' UNION
SELECT 2, '2014-12-10 00:00:00.000' UNION
SELECT 4, '2014-12-10 00:00:00.000' UNION
SELECT 5, '2014-12-10 00:00:00.000' UNION
SELECT 1, '2014-12-09 00:00:00.000' UNION
SELECT 2, '2014-12-09 00:00:00.000' UNION
SELECT 3, '2014-12-09 00:00:00.000'
)
SELECT DISTINCT E.[EmpID], M.[Date]
FROM EmployeeList E
CROSS APPLY (SELECT [Date] FROM MyCTE WHERE [Date] NOT IN (SELECT [Date] FROM EmployeeList WHERE EmpID = E.EmpID)) M