我的任务是创建一个报告,该报告根据系统登录和注销时间来估算建筑物的实际占用率。例如,如果员工1在0730登录,然后在1600注销,那么我需要统计一下,每隔半小时就有一位员工在场。我知道SO的不正确使用,但我实际上只是在寻求实现此目标的最佳实践。我正在为此使用的数据集非常大,因为他们正在寻求过去一年的每日价值。我的第一个想法是创建一个存储过程或一个UDF来完成此任务,但出于性能原因,宁愿使用直接SQL。
例如下面给出的示例数据
<table>
<thead><tr><th>EmployeeID</th><th>Date</th><th>Login</th><th>Logout</th></tr></thead><tbody>
<tr><td>123</td><td>10/31/2018</td><td>10/31/2018 8:30 AM</td><td>10/31/2018 10:35 AM</td></tr>
<tr><td>234</td><td>10/31/2018</td><td>10/31/2018 9:30 AM</td><td>10/31/2018 11:37 AM</td></tr>
<tr><td>345</td><td>10/31/2018</td><td>10/31/2018 9:00 AM</td><td>10/31/2018 11:09 AM</td></tr>
</tbody></table>
我希望以这种格式获取结果
<table>
<thead><tr><th>DATE</th><th>8:30:00 AM</th><th>9:00:00 AM</th><th>9:30:00 AM</th><th>10:00:00 AM</th><th>10:30:00 AM</th><th>11:00:00 AM</th><th>11:30:00 AM</th></tr></thead><tbody>
<tr><td>10/31/2018</td><td>1</td><td>2</td><td>3</td><td>3</td><td>3</td><td>2</td><td>1</td></tr>
</tbody></table>
答案 0 :(得分:2)
您可以尝试将CTE递归与条件汇总函数一起使用。
CTE递归为每个时间表创建日历表。然后将COUNT
与CASE WHEN
一起使用,作为时间量。
;WITH CTE AS (
SELECT [Date],Login,Logout
FROM T
UNION ALL
SELECT [Date],DATEADD(mi, 30, Login) ,Logout
FROM CTE
WHERE DATEADD(mi, 30, Login) < Logout
),CelanderCte AS(
SELECT *, CAST(Login AS TIME) LoginTitme
FROM CTE
)
SELECT convert(char(10), [Date], 101) 'DATE',
COUNT(CASE WHEN LoginTitme >= '8:30' AND LoginTitme < '9:00' THEN 1 END) '8:30:00 AM',
COUNT(CASE WHEN LoginTitme >= '9:00' AND LoginTitme < '9:30' THEN 1 END) '9:00:00 AM',
COUNT(CASE WHEN LoginTitme >= '9:30' AND LoginTitme < '10:00' THEN 1 END) '9:30:00 AM',
COUNT(CASE WHEN LoginTitme >= '10:00' AND LoginTitme < '10:30' THEN 1 END) '10:00:00 AM',
COUNT(CASE WHEN LoginTitme >= '10:30' AND LoginTitme <'11:00' THEN 1 END) '10:30:00 AM',
COUNT(CASE WHEN LoginTitme >= '11:00' AND LoginTitme <'11:30' THEN 1 END) '11:00:00 AM'
FROM CelanderCte
GROUP BY CONVERT(char(10), [Date],101)
结果
DATE 8:30:00 AM 9:00:00 AM 9:30:00 AM 10:00:00 AM 10:30:00 AM 11:00:00 AM
10/31/2018 1 2 3 3 3 2
答案 1 :(得分:1)
因为我已经把它写出来了...
/* ===================================================================
Start by creating some test data....
=================================================================== */
IF OBJECT_ID('tempdb..#LogTimeInfo', 'U') IS NULL
BEGIN -- DROP TABLE #LogTimeInfo;
CREATE TABLE #LogTimeInfo (
EmployeeID INT NOT NULL,
LoginDT DATETIME NOT NULL,
LogoutDT DATETIME NOT NULL,
PRIMARY KEY CLUSTERED (LoginDT, EmployeeID)
WITH (IGNORE_DUP_KEY = ON)
);
WITH
cte_n1 (n) AS (SELECT 1 FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) n (n)),
cte_n2 (n) AS (SELECT 1 FROM cte_n1 a CROSS JOIN cte_n1 b),
cte_n3 (n) AS (SELECT 1 FROM cte_n2 a CROSS JOIN cte_n2 b),
cte_Tally (n) AS (
SELECT TOP (1000000)
ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM
cte_n3 a CROSS JOIN cte_n3 b
)
INSERT #LogTimeInfo (EmployeeID, LoginDT, LogoutDT)
SELECT
ROW_NUMBER() OVER (PARTITION BY rd.rand_day ORDER BY t.n),
rit.rand_in_time,
rot.rand_out_time
FROM
cte_Tally t
CROSS APPLY ( VALUES (DATEADD(DAY, DATEDIFF(DAY, 0, GETDATE()), 0)) ) td (today)
CROSS APPLY ( VALUES (DATEADD(DAY, -1 * ABS(CHECKSUM(NEWID())) % 500, td.today)) ) rd (rand_day) -- 500 day range
CROSS APPLY ( VALUES (DATEADD(MINUTE, ABS(CHECKSUM(NEWID())) % 301 + 300, rd.rand_day)) ) rit (rand_in_time) -- between 5:00 am & 10: am
CROSS APPLY ( VALUES (DATEADD(MINUTE, ABS(CHECKSUM(NEWID())) % 301 + 300, rit.rand_in_time)) ) rot (rand_out_time); -- betweem 5 & 10 hours after clockin
END;
/* ===================================================================
The actual solution....
=================================================================== */
DECLARE @today DATETIME = CONVERT(DATE, GETDATE());
WITH -- Use the cte to create a virtual calendar table that has all dates within the desired date range.
cte_n1 (n) AS (SELECT 1 FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) n (n)),
cte_n2 (n) AS (SELECT 1 FROM cte_n1 a CROSS JOIN cte_n1 b),
cte_Dates (d) AS (
SELECT TOP (365)
DATEADD(DAY, ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) - 366, @today)
FROM
cte_n2 a CROSS JOIN cte_n2 b
)
SELECT
d.d,
[00:00] = COUNT(CASE WHEN lti.LoginDT < DATEADD(MINUTE, 30 , d.d) AND lti.LogoutDT > DATEADD(MINUTE, 0, d.d) THEN 1 END),
[00:30] = COUNT(CASE WHEN lti.LoginDT < DATEADD(MINUTE, 60 , d.d) AND lti.LogoutDT > DATEADD(MINUTE, 30, d.d) THEN 1 END),
[01:00] = COUNT(CASE WHEN lti.LoginDT < DATEADD(MINUTE, 90 , d.d) AND lti.LogoutDT > DATEADD(MINUTE, 60, d.d) THEN 1 END),
[01:30] = COUNT(CASE WHEN lti.LoginDT < DATEADD(MINUTE, 120 , d.d) AND lti.LogoutDT > DATEADD(MINUTE, 90, d.d) THEN 1 END),
[02:00] = COUNT(CASE WHEN lti.LoginDT < DATEADD(MINUTE, 150 , d.d) AND lti.LogoutDT > DATEADD(MINUTE, 120, d.d) THEN 1 END),
[02:30] = COUNT(CASE WHEN lti.LoginDT < DATEADD(MINUTE, 180 , d.d) AND lti.LogoutDT > DATEADD(MINUTE, 150, d.d) THEN 1 END),
[03:00] = COUNT(CASE WHEN lti.LoginDT < DATEADD(MINUTE, 210 , d.d) AND lti.LogoutDT > DATEADD(MINUTE, 180, d.d) THEN 1 END),
[03:30] = COUNT(CASE WHEN lti.LoginDT < DATEADD(MINUTE, 240 , d.d) AND lti.LogoutDT > DATEADD(MINUTE, 210, d.d) THEN 1 END),
[04:00] = COUNT(CASE WHEN lti.LoginDT < DATEADD(MINUTE, 270 , d.d) AND lti.LogoutDT > DATEADD(MINUTE, 240, d.d) THEN 1 END),
[04:30] = COUNT(CASE WHEN lti.LoginDT < DATEADD(MINUTE, 300 , d.d) AND lti.LogoutDT > DATEADD(MINUTE, 270, d.d) THEN 1 END),
[05:00] = COUNT(CASE WHEN lti.LoginDT < DATEADD(MINUTE, 330 , d.d) AND lti.LogoutDT > DATEADD(MINUTE, 300, d.d) THEN 1 END),
[05:30] = COUNT(CASE WHEN lti.LoginDT < DATEADD(MINUTE, 360 , d.d) AND lti.LogoutDT > DATEADD(MINUTE, 330, d.d) THEN 1 END),
[06:00] = COUNT(CASE WHEN lti.LoginDT < DATEADD(MINUTE, 390 , d.d) AND lti.LogoutDT > DATEADD(MINUTE, 360, d.d) THEN 1 END),
[06:30] = COUNT(CASE WHEN lti.LoginDT < DATEADD(MINUTE, 420 , d.d) AND lti.LogoutDT > DATEADD(MINUTE, 390, d.d) THEN 1 END),
[07:00] = COUNT(CASE WHEN lti.LoginDT < DATEADD(MINUTE, 450 , d.d) AND lti.LogoutDT > DATEADD(MINUTE, 420, d.d) THEN 1 END),
[07:30] = COUNT(CASE WHEN lti.LoginDT < DATEADD(MINUTE, 480 , d.d) AND lti.LogoutDT > DATEADD(MINUTE, 450, d.d) THEN 1 END),
[08:00] = COUNT(CASE WHEN lti.LoginDT < DATEADD(MINUTE, 510 , d.d) AND lti.LogoutDT > DATEADD(MINUTE, 480, d.d) THEN 1 END),
[08:30] = COUNT(CASE WHEN lti.LoginDT < DATEADD(MINUTE, 540 , d.d) AND lti.LogoutDT > DATEADD(MINUTE, 510, d.d) THEN 1 END),
[09:00] = COUNT(CASE WHEN lti.LoginDT < DATEADD(MINUTE, 570 , d.d) AND lti.LogoutDT > DATEADD(MINUTE, 540, d.d) THEN 1 END),
[09:30] = COUNT(CASE WHEN lti.LoginDT < DATEADD(MINUTE, 600 , d.d) AND lti.LogoutDT > DATEADD(MINUTE, 570, d.d) THEN 1 END),
[10:00] = COUNT(CASE WHEN lti.LoginDT < DATEADD(MINUTE, 630 , d.d) AND lti.LogoutDT > DATEADD(MINUTE, 600, d.d) THEN 1 END),
[10:30] = COUNT(CASE WHEN lti.LoginDT < DATEADD(MINUTE, 660 , d.d) AND lti.LogoutDT > DATEADD(MINUTE, 630, d.d) THEN 1 END),
[11:00] = COUNT(CASE WHEN lti.LoginDT < DATEADD(MINUTE, 690 , d.d) AND lti.LogoutDT > DATEADD(MINUTE, 660, d.d) THEN 1 END),
[11:30] = COUNT(CASE WHEN lti.LoginDT < DATEADD(MINUTE, 720 , d.d) AND lti.LogoutDT > DATEADD(MINUTE, 690, d.d) THEN 1 END),
[12:00] = COUNT(CASE WHEN lti.LoginDT < DATEADD(MINUTE, 750 , d.d) AND lti.LogoutDT > DATEADD(MINUTE, 720, d.d) THEN 1 END),
[12:30] = COUNT(CASE WHEN lti.LoginDT < DATEADD(MINUTE, 780 , d.d) AND lti.LogoutDT > DATEADD(MINUTE, 750, d.d) THEN 1 END),
[13:00] = COUNT(CASE WHEN lti.LoginDT < DATEADD(MINUTE, 810 , d.d) AND lti.LogoutDT > DATEADD(MINUTE, 780, d.d) THEN 1 END),
[13:30] = COUNT(CASE WHEN lti.LoginDT < DATEADD(MINUTE, 840 , d.d) AND lti.LogoutDT > DATEADD(MINUTE, 810, d.d) THEN 1 END),
[14:00] = COUNT(CASE WHEN lti.LoginDT < DATEADD(MINUTE, 870 , d.d) AND lti.LogoutDT > DATEADD(MINUTE, 840, d.d) THEN 1 END),
[14:30] = COUNT(CASE WHEN lti.LoginDT < DATEADD(MINUTE, 900 , d.d) AND lti.LogoutDT > DATEADD(MINUTE, 870, d.d) THEN 1 END),
[15:00] = COUNT(CASE WHEN lti.LoginDT < DATEADD(MINUTE, 930 , d.d) AND lti.LogoutDT > DATEADD(MINUTE, 900, d.d) THEN 1 END),
[15:30] = COUNT(CASE WHEN lti.LoginDT < DATEADD(MINUTE, 960 , d.d) AND lti.LogoutDT > DATEADD(MINUTE, 930, d.d) THEN 1 END),
[16:00] = COUNT(CASE WHEN lti.LoginDT < DATEADD(MINUTE, 990 , d.d) AND lti.LogoutDT > DATEADD(MINUTE, 960, d.d) THEN 1 END),
[16:30] = COUNT(CASE WHEN lti.LoginDT < DATEADD(MINUTE, 1020 , d.d) AND lti.LogoutDT > DATEADD(MINUTE, 990, d.d) THEN 1 END),
[17:00] = COUNT(CASE WHEN lti.LoginDT < DATEADD(MINUTE, 1050 , d.d) AND lti.LogoutDT > DATEADD(MINUTE, 1020, d.d) THEN 1 END),
[17:30] = COUNT(CASE WHEN lti.LoginDT < DATEADD(MINUTE, 1080 , d.d) AND lti.LogoutDT > DATEADD(MINUTE, 1050, d.d) THEN 1 END),
[18:00] = COUNT(CASE WHEN lti.LoginDT < DATEADD(MINUTE, 1110 , d.d) AND lti.LogoutDT > DATEADD(MINUTE, 1080, d.d) THEN 1 END),
[18:30] = COUNT(CASE WHEN lti.LoginDT < DATEADD(MINUTE, 1140 , d.d) AND lti.LogoutDT > DATEADD(MINUTE, 1110, d.d) THEN 1 END),
[19:00] = COUNT(CASE WHEN lti.LoginDT < DATEADD(MINUTE, 1170 , d.d) AND lti.LogoutDT > DATEADD(MINUTE, 1140, d.d) THEN 1 END),
[19:30] = COUNT(CASE WHEN lti.LoginDT < DATEADD(MINUTE, 1200 , d.d) AND lti.LogoutDT > DATEADD(MINUTE, 1170, d.d) THEN 1 END),
[20:00] = COUNT(CASE WHEN lti.LoginDT < DATEADD(MINUTE, 1230 , d.d) AND lti.LogoutDT > DATEADD(MINUTE, 1200, d.d) THEN 1 END),
[20:30] = COUNT(CASE WHEN lti.LoginDT < DATEADD(MINUTE, 1260 , d.d) AND lti.LogoutDT > DATEADD(MINUTE, 1230, d.d) THEN 1 END),
[21:00] = COUNT(CASE WHEN lti.LoginDT < DATEADD(MINUTE, 1290 , d.d) AND lti.LogoutDT > DATEADD(MINUTE, 1260, d.d) THEN 1 END),
[21:30] = COUNT(CASE WHEN lti.LoginDT < DATEADD(MINUTE, 1320 , d.d) AND lti.LogoutDT > DATEADD(MINUTE, 1290, d.d) THEN 1 END),
[22:00] = COUNT(CASE WHEN lti.LoginDT < DATEADD(MINUTE, 1350 , d.d) AND lti.LogoutDT > DATEADD(MINUTE, 1320, d.d) THEN 1 END),
[22:30] = COUNT(CASE WHEN lti.LoginDT < DATEADD(MINUTE, 1380 , d.d) AND lti.LogoutDT > DATEADD(MINUTE, 1350, d.d) THEN 1 END),
[23:00] = COUNT(CASE WHEN lti.LoginDT < DATEADD(MINUTE, 1410 , d.d) AND lti.LogoutDT > DATEADD(MINUTE, 1380, d.d) THEN 1 END),
[23:30] = COUNT(CASE WHEN lti.LoginDT < DATEADD(MINUTE, 1440 , d.d) AND lti.LogoutDT > DATEADD(MINUTE, 1410, d.d) THEN 1 END)
FROM
cte_Dates d
LEFT JOIN #LogTimeInfo lti
ON d.d <= lti.LoginDT
AND DATEADD(DAY, 1, d.d) > lti.LoginDT
GROUP BY
d.d;