how to get header and detail row from a result set? - sql-server

I have these three tables .
CREATE TABLE Employees (
EmpID INT IDENTITY,
EmpName VARCHAR(100)
)
GO
INSERT INTO Employees(EmpName)
SELECT 'John Torres' UNION ALL
SELECT 'Irina Williams'
SELECT * FROM Employees
GO
CREATE TABLE PayrollWeek(
WeekID INT IDENTITY,
EmpID INT,
WeekStart DATETIME,
WeekEnd DATETIME
)
GO
INSERT INTO PayrollWeek(EmpID,WeekStart,WeekEnd)
SELECT 1,'11-20-2011','11-26-2011' UNION ALL
SELECT 2,'11-27-2011','12-03-2011' UNION ALL
SELECT 1,'11-27-2011','12-03-2011'
SELECT * FROM PayrollWeek
GO
CREATE TABLE EmployeeVisits(
ID INT,
EmpID INT,
VisitDate DATETIME,
StartTime VARCHAR(5),
EndTime VARCHAR(5),
EarningCode VARCHAR(100)
)
GO
INSERT INTO EmployeeVisits(ID,EmpID,VisitDate,StartTime,EndTime,EarningCode)
SELECT 1,1,'11-20-2011','10:00','12:00','Sat-Sun1' UNION ALL
SELECT 2,1,'11-21-2011','13:30','16:00','Mon-Fri1' UNION ALL
SELECT 3,1,'11-22-2011','14:00','15:00','Mon-Fri1' UNION ALL
SELECT 4,1,'11-24-2011','10:00','14:00','Mon-Fri1' UNION ALL
SELECT 5,1,'11-25-2011','13:30','16:00','Mon-Fri1' UNION ALL
SELECT 6,1,'11-26-2011','14:00','15:00','Sat-Sun1' UNION ALL
SELECT 7,2,'11-27-2011','09:00','11:00','Sat-Sun1' UNION ALL
SELECT 8,2,'11-28-2011','07:00','12:00','Mon-Fri1' UNION ALL
SELECT 9,2,'11-29-2011','09:00','11:00','Mon-Fri1' UNION ALL
SELECT 10,2,'12-03-2011','07:00','12:00','Sat-Sun1'
Expected Result is this
RecordType EmpId EmpName WeekStart Weekend EarningCode Hour
H 1 John Torres 11/20/2011 11/26/2011
D   Sat-Sun1 3
D Mon-Fri1 10
H 2  Irina Williams 11/27/2011 12/3/2011
D   Sat-Sun1 7
D Mon-Fri1 7

Here is my answer I try this using Union, Group By .
SELECT 'H' AS RecordType
,emp.EmpID
,EmpName
,Cast(MIN(WeekStart) AS VARCHAR(106)) AS StartTime
,cast(Max(VisitDate) AS VARCHAR(106)) AS EndTime
,'' AS EarningCode
,'' AS TimeDiff
FROM EmployeeVisits E
JOIN PayrollWeek P ON e.EmpID = P.EmpID
JOIN Employees Emp ON Emp.EmpID = e.EmpID
GROUP BY emp.EmpID
,EmpName
UNION
SELECT 'D' AS RecordType
,EmpID
,'' AS EmpName
,'' AS StartTime
,'' AS EndTime
,EarningCode
,cast(sum(dateDiff(MINUTE, StartTime, EndTime)) / 60 AS VARCHAR(100)) AS TimeDiff
FROM EmployeeVisits
GROUP BY EmpID
,EarningCode
ORDER BY EmpID
,RecordType DESC

Related

how can i know the total number of holidays by employee

I have this tables Holiday(Id,FK(EmployeeId),StartDate,EndDate) and table Employee(Id,FullName,etc...)
I want to know the number of days that each employee have
I was trying something like this :
SELECT Employee.Id, SUM(DATEDIFF(day,Holiday.StartDate,Holiday.EndDate) + 1)
FROM Employee
LEFT JOIN Holiday ON Holiday.EmployeeId=Employee.Id
GROUP BY Employee.id
i know this doesn't work because, to sum thati would need to group by Holiday.Id since i will have many rows in the Holiday table for the same EmployeeId
how can i accomplish this?
thanks for the help
Or, using a workingday calculation I found here: https://www.sqlshack.com/how-to-calculate-work-days-and-hours-in-sql-server you could do the following:
CREATE FUNCTION workingdays ( #DateFrom Date, #DateTo Date) RETURNS INT AS
BEGIN
DECLARE #TotDays INT= DATEDIFF(DAY, #DateFrom, #DateTo) + 1;
DECLARE #TotWeeks INT= DATEDIFF(WEEK, #DateFrom, #DateTo) * 2;
DECLARE #IsSunday INT= CASE
WHEN DATENAME(WEEKDAY, #DateFrom) = 'Sunday'
THEN 1
ELSE 0
END;
DECLARE #IsSaturday INT= CASE
WHEN DATENAME(WEEKDAY, #DateTo) = 'Saturday'
THEN 1
ELSE 0
END;
DECLARE #TotWorkingDays INT= #TotDays - #TotWeeks - #IsSunday + #IsSaturday;
RETURN #TotWorkingDays;
END
GO
create table Employee (Id int identity, name varchar(64), Primary Key (Id));
create table Holiday (EmployeeId int, StartDate date, EndDate date);
insert into Employee VALUES ('Harry Potter'),('Hermiony Granger'),('Ron Weasly'),('Ginny Weasley');
insert into Holiday VALUES (1,'2020-02-12','2020-02-18'),(1,'2020-04-02','2020-04-07'),(1,'2020-08-21','2020-09-05'),
(2,'2020-01-04','2020-01-13'),(2,'2020-03-17','2020-03-23'),(2,'2020-05-29','2020-06-7');
SELECT Employee.Id, SUM(dbo.workingdays(Holiday.StartDate,Holiday.EndDate))
FROM Employee
LEFT JOIN Holiday ON Holiday.EmployeeId=Employee.Id
GROUP BY Employee.id
This will still only be a crude estimation as is does not account for public holidays.
DEMO: https://rextester.com/QKGUT41272
Hi i think you can build query using CTE like this :
Resource : CTE Microsoft : https://learn.microsoft.com/fr-fr/sql/t-sql/queries/with-common-table-expression-transact-sql?view=sql-server-ver15
Or using select in clause form
EDIT : Or it work with your current query to
create Table #EMPLOYEE
(
EMP_ID INT,
EMP_NAME varchar(128)
)
create Table #HOLIDAYS
(
HL_ID INT,
HL_EMP varchar(128),
StartDate DATE,
EndDate DATE
)
Insert into #EMPLOYEE
(
EMP_ID,
EMP_NAME
)
SELECT 1, 'Toto'
UNION ALL
SELECT 2,'Dupont'
UNION ALL
SELECT 3,'Titi'
UNION ALL
SELECT 4,'Tata'
Insert into #HOLIDAYS
(
HL_ID,
HL_EMP,
StartDate,
EndDate
)
SELECT '1', '1',GETDATE(),DATEADD(day,4,GETDATE())
UNION ALL
SELECT '2','1',DATEADD(day,-7,GETDATE()), DATEADD(day,-5,GETDATE())
UNION ALL
SELECT '3','2',DATEADD(day,4,GETDATE()),DATEADD(day,15,GETDATE())
-- USING CTE EXEMPLE
;WITH MyCteAPP AS (
SELECT EMP_ID, EMP_NAME, HL_ID, ISNULL(StartDate,GETDATE()) AS 'StartDate', ISNULL(EndDate,GETDATE()) AS 'EndDate'
FROM #EMPLOYEE
LEFT JOIN #HOLIDAYS ON EMP_ID = HL_EMP
)
SELECT EMP_ID, EMP_NAME, SUM(DATEDIFF(day,StartDate,EndDate)) AS 'NbDay'
FROM MyCteAPP
GROUP BY EMP_ID,EMP_NAME
ORDER BY EMP_ID
-- USING SELECT IN FROM EXEMPLE
SELECT EMP_ID, EMP_NAME, SUM(DATEDIFF(day,StartDate,EndDate)) AS 'NbDay'
FROM (SELECT EMP_ID, EMP_NAME, HL_ID, ISNULL(StartDate,GETDATE()) AS 'StartDate', ISNULL(EndDate,GETDATE()) AS 'EndDate'
FROM #EMPLOYEE
LEFT JOIN #HOLIDAYS ON EMP_ID = HL_EMP
) AS SUBQUERY
GROUP BY EMP_ID,EMP_NAME
ORDER BY EMP_ID
--USING CURRENT QUERY
SELECT EMP_ID, EMP_NAME, SUM(DATEDIFF(day,ISNULL(StartDate,GETDATE()),ISNULL(EndDate,GETDATE()))) AS 'NbDay'
FROM #EMPLOYEE
LEFT JOIN #HOLIDAYS ON EMP_ID = HL_EMP
GROUP BY EMP_ID,EMP_NAME
ORDER BY EMP_ID
DROP TABLE #EMPLOYEE
DROP TABLE #HOLIDAYS
RESULT :
I have tried with sub query. Please use below query. It will be helpful.
SELECT Id, SUM(leave) AS leave
FROM
(
SELECT Employee.Id, DATEDIFF(dd,Holiday.StartDate,Holiday.EndDate) as leave
FROM Employee
LEFT JOIN Holiday ON Holiday.EmployeeId=Employee.Id
)a
GROUP BY ID

Find absent dates of employee and one date before & after present

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
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.
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

How to display tabular record of dates from date range

I have Request table with 3 records having structure: Id, DateFrom, DateTo
Id DateFrom DateTo
1 15/01/2019 15/01/2019
2 21/01/2019 28/01/2019
3 04/02/2019 09/02/2019
And I want an output like this:
Id Date
1 15/01/2019
2 21/01/2019
2 22/01/2019
2 23/01/2019
2 24/01/2019
2 25/01/2019
2 26/01/2019
2 27/01/2019
2 28/01/2019
3 04/02/2019
3 05/02/2019
3 06/02/2019
3 07/02/2019
3 08/02/2019
3 09/02/2019
I have created a table valued function to display the series of date based DateFrom and DateTo.
CREATE FUNCTION [dbo].[tvfhrms_Calendar_DateRange](#DateFrom date, #DateTo date)
RETURNS #DateOfTheYear Table(Level int,SysDate date)
AS
BEGIN
WITH AllDays
AS (
SELECT [Level] = 1
,[Date] = #DateFrom
UNION ALL
SELECT [Level] = [Level] + 1
,[Date] = DATEADD(DAY, 1, [Date])
FROM AllDays
WHERE [Date] < #DateTo
)
INSERT #DateOfTheYear
SELECT [Level]
,[SysDate]=[Date]
FROM AllDays OPTION (MAXRECURSION 0)
RETURN
END
Then when used in select query,
SELECT sysdate from [dbo].[tvfhrms_Calendar_DateRange]('2019-01-10', '2019-02-09')
This will give the results of the sequence of Datefrom to DateTo.
How can I integrate this to my table so that I can have the output as my expectations?
You can use APPLY :
SELECT tt.*
FROM table t CROSS APPLY
(SELECT tt.*
FROM [dbo].[tvfhrms_Calendar_DateRange] (t.datefrom, t.dateto) AS tt
) tt;
No need to have extra table with dates. Note that my dates in different format.
DECLARE #t TABLE (Id INT, DateFrom DATE, DateTo DATE)
INSERT INTO #t VALUES
(1,'01/15/2019','01/15/2019'),
(2,'01/21/2019','01/28/2019'),
(3,'02/04/2019','02/09/2019')
;WITH cte as (
SELECT ID, [Date] = DateFrom FROM #t
UNION ALL
SELECT t.ID, DATEADD(DAY,1,[Date]) FROM #t as t
INNER JOIN cte ON t.ID = cte.ID and cte.[Date] < t.DateTo
)
SELECT * FROM cte
ORDER BY ID

SQL Server 2008 R2: How to get Free spaces Between times

I have two columns in a table that represent start and end for an employee work time (It Stores HH:MM:SS). I have another table working as Stack where I store the Busy time. Let's say I stored this as a result for an employee where start is 8:00:00 and end is 17:00:00
startColumn endColumn
08:15:00.0000000 08:45:00.0000000
11:00:00.0000000 12:00:00.0000000
12:00:00.0000000 13:00:00.0000000
I want to get the following as a result considering the above data is in the table:
8:00 A.M
8:45 A.M
10:00 A.M
1:00 P.M
2:00 P.M
3:00 P.M
4:00 P.M
Thanks in advance!
EDIT: Here is a solution with quarters of an hour, coming back with all quarters without working activities...
DECLARE #empl TABLE(ID INT, EmplName VARCHAR(100));
INSERT INTO #empl VALUES
(1,'Mr. X');
DECLARE #emplStandard TABLE(emplID INT, StartHour TIME, EndHour TIME);
INSERT INTO #emplStandard VALUES
(1,{t'08:00:00'},{t'17:00:00'});
DECLARE #Work TABLE(emplID INT,WorkDay DATE,StartHour TIME,EndHour TIME);
INSERT INTO #Work VALUES
(1,{d'2016-02-05'},{t'08:15:00'},{t'08:45:00'})
,(1,{d'2016-02-05'},{t'11:00:00'},{t'12:00:00'})
,(1,{d'2016-02-05'},{t'12:00:00'},{t'13:00:00'});
WITH TallyTimes(TheHour) AS
(
SELECT {t'08:00:00'}
UNION SELECT {t'08:15:00'}
UNION SELECT {t'08:30:00'}
UNION SELECT {t'08:45:00'}
UNION SELECT {t'09:00:00'}
UNION SELECT {t'09:15:00'}
UNION SELECT {t'09:30:00'}
UNION SELECT {t'09:45:00'}
UNION SELECT {t'10:00:00'}
UNION SELECT {t'10:15:00'}
UNION SELECT {t'10:30:00'}
UNION SELECT {t'10:45:00'}
UNION SELECT {t'11:00:00'}
UNION SELECT {t'11:15:00'}
UNION SELECT {t'11:30:00'}
UNION SELECT {t'11:45:00'}
UNION SELECT {t'12:00:00'}
UNION SELECT {t'12:15:00'}
UNION SELECT {t'12:30:00'}
UNION SELECT {t'12:45:00'}
UNION SELECT {t'13:00:00'}
UNION SELECT {t'13:15:00'}
UNION SELECT {t'13:30:00'}
UNION SELECT {t'13:45:00'}
UNION SELECT {t'14:00:00'}
UNION SELECT {t'14:15:00'}
UNION SELECT {t'15:30:00'}
UNION SELECT {t'16:45:00'}
UNION SELECT {t'15:00:00'}
UNION SELECT {t'15:15:00'}
UNION SELECT {t'15:30:00'}
UNION SELECT {t'15:45:00'}
UNION SELECT {t'16:00:00'}
UNION SELECT {t'16:15:00'}
UNION SELECT {t'16:30:00'}
UNION SELECT {t'16:45:00'}
UNION SELECT {t'17:00:00'}
UNION SELECT {t'17:15:00'}
UNION SELECT {t'17:30:00'}
UNION SELECT {t'17:45:00'}
)
SELECT e.EmplName
,CAST(CAST(tt.TheHour AS TIME) AS VARCHAR(8)) AS TheHour
FROM TallyTimes AS tt
CROSS JOIN #empl AS e
INNER JOIN #emplStandard AS es ON es.emplID=e.ID
WHERE NOT EXISTS(SELECT 1
FROM #Work AS w
WHERE w.emplID=e.ID
AND CAST(tt.TheHour AS TIME)>=w.StartHour AND CAST(tt.TheHour AS TIME)<=w.EndHour
)
Following the first attempt (before the question was changed)
If I understand you correctly you are dealing with full hours only:
This will pick out the hours your employee was working:
A short explanation: After filling your test data in declared table variables I start with a CTE creating a list of all hours from 06:00 to 20:00. The final SELECT checks whether exists an entry within the table "Work" where the current hour is within the intervall or not. It's on you to decide, if the EndHour is included or not. Your own sample data are inconsistent in this point of view.
You might shift the CASE WHEN-logic into a WHERE if you really want the non-working hours only...
And be aware, that you will need further logic to distinguish between different employees, WorkDays and maybe differing time standards for each WorkDay (Friday other than Monday...)
DECLARE #empl TABLE(ID INT, EmplName VARCHAR(100));
INSERT INTO #empl VALUES
(1,'Mr. X');
DECLARE #emplStandard TABLE(emplID INT, StartHour TIME, EndHour TIME);
INSERT INTO #emplStandard VALUES
(1,{t'08:00:00'},{t'17:00:00'});
DECLARE #Work TABLE(emplID INT,WorkDay DATE,StartHour TIME,EndHour TIME);
INSERT INTO #Work VALUES
(1,{d'2016-02-05'},{t'08:00:00'},{t'09:00:00'})
,(1,{d'2016-02-05'},{t'11:00:00'},{t'12:00:00'})
,(1,{d'2016-02-05'},{t'12:00:00'},{t'13:00:00'});
WITH TallyTimes(TheHour) AS
(
SELECT {t'06:00:00'}
UNION SELECT {t'07:00:00'}
UNION SELECT {t'08:00:00'}
UNION SELECT {t'09:00:00'}
UNION SELECT {t'10:00:00'}
UNION SELECT {t'11:00:00'}
UNION SELECT {t'12:00:00'}
UNION SELECT {t'13:00:00'}
UNION SELECT {t'14:00:00'}
UNION SELECT {t'15:00:00'}
UNION SELECT {t'16:00:00'}
UNION SELECT {t'17:00:00'}
UNION SELECT {t'18:00:00'}
UNION SELECT {t'19:00:00'}
UNION SELECT {t'20:00:00'}
)
SELECT e.EmplName
,tt.TheHour
,CASE WHEN EXISTS(SELECT 1
FROM #Work AS w
WHERE w.emplID=e.ID
AND CAST(tt.TheHour AS TIME)>=w.StartHour AND CAST(tt.TheHour AS TIME)<=w.EndHour
) THEN 'X' ELSE '' END
FROM TallyTimes AS tt
CROSS JOIN #empl AS e
INNER JOIN #emplStandard AS es ON es.emplID=e.ID
The result:
Empl TheHour WasWorking
Mr. X 06:00:00
Mr. X 07:00:00
Mr. X 08:00:00 X
Mr. X 09:00:00 X
Mr. X 10:00:00
Mr. X 11:00:00 X
Mr. X 12:00:00 X
Mr. X 13:00:00 X
Mr. X 14:00:00
Mr. X 15:00:00
Mr. X 16:00:00
Mr. X 17:00:00
Mr. X 18:00:00
Mr. X 19:00:00
Mr. X 20:00:00
For anyone else looking for the same here is what i did
DECLARE #Contador INT
DECLARE #start TIME
DECLARE #end TIME
DECLARE #request INT
DECLARE #Max INT
DECLARE #Libres TABLE(Id INT,startTime TIME, endTime TIME)
DECLARE #Result TABLE(Id INT,Horario VARCHAR(8))
SET #Contador = 1
INSERT INTO #Libres VALUES (1,'08:00:00.0000000','10:00:00.0000000');
INSERT INTO #Libres VALUES (2,'11:00:00.0000000','14:00:00.0000000');
INSERT INTO #Libres VALUES (3,'14:00:00.0000000','15:00:00.0000000');
SET #Max = (SELECT MAX(id) Id FROM #Libres)
WHILE(#Contador <= (SELECT COUNT(*) FROM #Libres))
BEGIN
SET #START = (SELECT startTime FROM #Libres Where Id = #Contador)
SET #END = (SELECT DATEADD(MINUTE, -60 ,endTime) FROM #Libres Where Id = #Contador)
SET #REQUEST = 1
;WITH Dates AS (
SELECT #request AS Id,#Start AS reqDate
UNION all
SELECT Id+1,DATEADD(MINUTE,60,reqDate) FROM Dates
WHERE reqDate < #end
)
INSERT INTO #Result(Id,Horario)
SELECT Id,CONVERT(VARCHAR(8),reqDate,100) "Horarios" FROM Dates
SET #Contador = #Contador + 1
IF(#Max < #Contador)
BREAK;
ELSE
CONTINUE;
END
SELECT * FROM #Result

Trying to pivot event dates in t-sql without using a cursor

I have the following table:
What I want is to get to this:
EventTypeId 1 and 3 are valid start events and EventTypeId of 2 is the only valid end event.
I have tried to do a pivot, but I don't believe a pivot will get me the multiple events for a person in the result set.
SELECT PersonId, [1],[3],[2]
FROM
(
SELECT PersonId, EventTypeId, EventDate
from #PersonEvent
) as SourceTable
PIVOT
(
count(EventDate) FOR EventTypeId
IN ([1],[3],[2])
) as PivotTable
Select PersonID,
Min(Case WHEN EventTypeId IN (1,3) THEN EventDate END) as StartDate,
Min(Case WHEN EventTypeId IN (2) THEN EventDate END) as EndDate
FROM #PersonEvent
group by personid
I can do a cursor, but my original table is over 90,000 rows, and this is to be for a report, so I don't think I can use that option. Any other thoughts that I might be missing?
Assuming the table is called [dbo].[PersonEventRecords] this will work...
With StartEvents As
(
Select *
From [dbo].[PersonEventRecords]
Where EventTypeId In (1,3)
), EndEvents As
(
Select *
From [dbo].[PersonEventRecords]
Where EventTypeId In (2)
)
Select IsNull(se.PersonId,ee.PersonId) As PersonId,
se.EventTypeId As StartEventTypeId,
se.EventDate As StartEventDate,
ee.EventTypeId As EndEventTypeId,
ee.EventDate As EndEventDate
From StartEvents se
Full Outer Join EndEvents ee
On se.PersonId = ee.PersonId
And se.EventSequence = ee.EventSequence - 1
Order By IsNull(se.PersonId,ee.PersonId),
IsNull(se.EventDate,ee.EventDate);
/**** TEST DATA ****/
If Object_ID('[dbo].[PersonEventRecords]') Is Not Null
Drop Table [dbo].[PersonEventRecords];
Create Table [dbo].[PersonEventRecords]
(
PersonId Int,
EventTypeId Int,
EventDate Date,
EventSequence Int
);
Insert [dbo].[PersonEventRecords]
Select 1,1,'2012-10-13',1
Union All
Select 1,2,'2012-10-20',2
Union All
Select 1,1,'2012-11-01',3
Union All
Select 1,2,'2012-11-13',4
Union All
Select 2,1,'2012-05-07',1
Union All
Select 2,2,'2012-06-01',2
Union All
Select 2,3,'2012-07-01',3
Union All
Select 2,2,'2012-08-30',4
Union All
Select 3,2,'2012-04-05',1
Union All
Select 3,1,'2012-05-04',2
Union All
Select 3,2,'2012-05-24',3
Union All
Select 4,1,'2013-01-03',1
Union All
Select 4,1,'2013-02-20',2
Union All
Select 4,2,'2013-03-20',3;
Try this
SELECT E1.PersonId, E1.EventTypeId, E1.EventDate, E2.EventTypeId, E2.EventDate
FROM PersonEvent AS E1
OUTER APPLY(
SELECT TOP 1 PersonEvent.EventTypeId, PersonEvent.EventDate
FROM PersonEvent
WHERE PersonEvent.PersonId = E1.PersonId
AND PersonEvent.EventSequence = E1.EventSequence + 1
AND PersonEvent.EventTypeId = 2
) AS E2
WHERE E1.EventTypeId = 1 OR E1.EventTypeId = 3
UNION
SELECT E3.PersonId, NULL, NULL, E3.EventTypeId, E3.EventDate
FROM PersonEvent E3
WHERE E3.EventTypeId = 2
AND NOT EXISTS(
SELECT *
FROM PersonEvent
WHERE PersonEvent.PersonId = E3.PersonId
AND PersonEvent.EventSequence = E3.EventSequence - 1)
It is not completely clear how do you want the result to be ordered – add order as needed.

Resources