Reputation: 7270
I have the following sample data:
--Table 1:
CREATE TABLE tbl_Emp_1
(
EmpID INT,
ColDate DATE
);
INSERT INTO tbl_Emp_1 VALUES(1,'2019-11-01');
INSERT INTO tbl_Emp_1 VALUES(2,'2019-11-02');
INSERT INTO tbl_Emp_1 VALUES(3,'2019-11-11');
INSERT INTO tbl_Emp_1 VALUES(4,'2019-11-12');
INSERT INTO tbl_Emp_1 VALUES(9,'2019-11-13');
INSERT INTO tbl_Emp_1 VALUES(6,'2019-11-16');
INSERT INTO tbl_Emp_1 VALUES(408,'2019-11-25');
--Table 2:
CREATE TABLE tbl_Emp_2
(
EmpID INT,
ColDate DATE
);
INSERT INTO tbl_Emp_2 VALUES(11,'2019-11-02');
INSERT INTO tbl_Emp_2 VALUES(22,'2019-11-06');
INSERT INTO tbl_Emp_2 VALUES(22,'2019-11-08');
INSERT INTO tbl_Emp_2 VALUES(33,'2019-11-10');
INSERT INTO tbl_Emp_2 VALUES(44,'2019-11-15');
--Table 3:
CREATE TABLE tbl_Emp_3
(
EmpID INT,
ColDate DATE
);
INSERT INTO tbl_Emp_3 VALUES(111,'2019-11-12');
INSERT INTO tbl_Emp_3 VALUES(222,'2019-11-16');
INSERT INTO tbl_Emp_3 VALUES(333,'2019-11-17');
INSERT INTO tbl_Emp_3 VALUES(444,'2019-11-19');
INSERT INTO tbl_Emp_3 VALUES(5,'2019-11-22');
--Now I will create View of these tables.
CREATE VIEW vw_Emp AS
SELECT *,1 AS TableID FROM tbl_Emp_1
UNION ALL
SELECT *,2 AS TableID FROM tbl_Emp_2
UNION ALL
SELECT *,3 AS TableID FROM tbl_Emp_3;
Expected Output:
EmpID ColDate
--------------------------------
2 2019-11-02 ---TABLE 1 Starts
NULL 2019-11-03 - 2019-11-10
3 2019-11-11
9 2019-11-13
NULL 2019-11-14 - 2019-11-15
6 2019-11-16
NULL 2019-11-17 - 2019-11-24
408 2019-11-25
11 2019-11-02 ---TABLE 2 Data Starts
NULL 2019-11-03 - 2019-11-05
22 2019-11-06
NULL 2019-11-07
22 2019-11-08
NULL 2019-11-09
33 2019-11-10
NULL 2019-11-11 - 2019-11-14
44 2019-11-15
111 2019-11-12 ---TABLE 3 Data Starts
NULL 2019-11-13 - 2019-11-15
222 2019-11-16
333 2019-11-17
NULL 2019-11-18
444 2019-11-19
NULL 2019-11-20 - 2019-11-21
5 2019-11-22
About the output: Display absent dates of Employee and display emp data of one date before and after those dates(employee not exists dates).
My try:
DECLARE @TableID INT,
@MinDate DATE,
@MaxDate DATE;
DECLARE Cur_Get_MinMax1 CURSOR FOR
SELECT TableID,
(SELECT MIN(ColDate) FROM vw_Emp WHERE TableID = v1.TableID),
(SELECT MAX(ColDate) FROM vw_Emp WHERE TableID = v1.TableID)
FROM vw_Emp v1
GROUP BY TableID;
IF OBJECT_ID('tempdb..#TempEmpData') IS NOT NULL
DROP TABLE #TempEmpData;
CREATE TABLE #TempEmpData
(
Dates DATE,
TableID int
);
OPEN Cur_Get_MinMax1;
FETCH NEXT FROM Cur_Get_MinMax1 INTO
@TableID,
@MinDate,
@MaxDate;
WHILE @@FETCH_STATUS = 0
BEGIN
PRINT(@TableID);
PRINT(@MinDate);
PRINT(@MaxDate);
INSERT INTO #TempEmpData
SELECT TOP (DATEDIFF(DAY, @MinDate, @MaxDate) + 1)
Date = DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY a.object_id) - 1, @MinDate),
@TableID
FROM sys.all_objects a
CROSS JOIN sys.all_objects b
FETCH NEXT FROM Cur_Get_MinMax1 INTO
@TableID,
@MinDate,
@MaxDate;
END;
CLOSE Cur_Get_MinMax1;
DEALLOCATE Cur_Get_MinMax1;
Query 1:
SELECT v.EmpID,t.Dates
FROM #TempEmpData t
LEFT JOIN vw_Emp v ON v.ColDate = t.Dates AND v.TableID = t.TableID
ORDER BY t.TableID,t.Dates;
Edit:
Query 2:
;WITH CTE AS
(
SELECT DISTINCT TableID,Dates,EmpID,
coalesce(stuff((select distinct CAST(MIN(Dates) as varchar(10))+'~'+ CAST(MAX(Dates) as varchar(10)) from #TempEmpData t1 where a.rr = 1 AND t1.Dates=a.Dates for xml path('')),1,0,''),cast(Dates as varchar(10))) Coldate
FROM
(
SELECT v.EmpID,
t.Dates,
t.TableID,
RANK() OVER(ORDER BY v.EmpID) rr
FROM vw_Emp v
RIGHT JOIN #TempEmpData t ON v.ColDate = t.Dates AND v.TableID = t.TableID
GROUP BY t.TableID,v.EmpID,t.Dates,v.TableID
) a
)
SELECT EmpID,ColDate
FROM CTE
ORDER BY TableID,Dates
Upvotes: 0
Views: 241
Reputation: 24813
this uses window function LAG()
and LEAD()
to find previous and next ColDate
based on ColDate
ordering.
The first query returns the before and after row when a discontinued date is encounter. The second query returns the date range of the discontinued date.
; with
tbl_Emp as
(
select tbl = 1, EmpID, ColDate from tbl_Emp_1
union all
select tbl = 2, EmpID, ColDate from tbl_Emp_2
union all
select tbl = 3, EmpID, ColDate from tbl_Emp_3
),
cte as
(
select *,
prevColDate = LAG(ColDate) over (partition by tbl order by ColDate),
nextColDate = LEAD(ColDate) over (partition by tbl order by ColDate)
from tbl_Emp
)
-- first query
select c.tbl,
c.EmpID,
c.ColDate,
EndDate = NULL
from cte c
where c.ColDate <> dateadd(day, +1, prevColDate)
or c.ColDate <> dateadd(day, -1, nextColDate)
union all
-- second query
select c.tbl,
EmpID = NULL,
ColDate = dateadd(day, 1, c.ColDate),
EndDate = dateadd(day, -1, nextColDate)
from cte c
where c.ColDate <> dateadd(day, -1, nextColDate)
order by tbl, ColDate;
Note : i didn't concatenate the ColDate
and EndDate
as what you have shown in your expected result.
Upvotes: 1
Reputation: 6808
SELECT CAST(NULL AS INT) AS EmpId, DATEADD(day, 1, PreviousDate) AS StartDate, DATEADD(day, -1, ColDate) AS EndDate
FROM
(
SELECT ColDate, LAG(ColDate) OVER(ORDER BY ColDate) AS PreviousDate, LEAD(ColDate) OVER(ORDER BY ColDate) AS NextDate
FROM (SELECT DISTINCT ColDate FROM dbo.tbl_Emp_1) AS src
) AS thedates
WHERE ColDate <> DATEADD(day, 1, PreviousDate)
SELECT CAST(NULL AS INT) AS EmpId, StartDate, EndDate
FROM
(
SELECT DATEADD(day, 1, sd.StartDate) AS StartDate, DATEADD(day, -1, MIN(ed.EndDate)) AS EndDate
FROM
(
--start dates of missing ranges
SELECT ColDate AS StartDate
FROM dbo.tbl_Emp_1 as a
WHERE NOT EXISTS(SELECT * FROM dbo.tbl_Emp_1 AS b WHERE b.ColDate = DATEADD(day, 1, a.ColDate))
) AS sd
JOIN
(
--end dates of missing ranges
SELECT ColDate AS EndDate
FROM dbo.tbl_Emp_1 as a
WHERE NOT EXISTS(SELECT * FROM dbo.tbl_Emp_1 AS b WHERE b.ColDate = DATEADD(day, -1, a.ColDate))
) AS ed ON sd.StartDate < ed.EndDate
GROUP BY sd.StartDate
) AS emptyperiods
Upvotes: 1