使用 CTE 按天和小时捕获体积计数
Use CTE to capture volume counts by day and hour
我想根据他们注册的开始日期和出院日期,按星期几和按小时查看当前患者数量。
例如:John doe 开始日期:01-01-2022 13:00:00 ;结束日期 01-01-2022 16:25:00
我希望数据显示 John doe 在设施中的每个小时。所以输出看起来像这样:
John Doe 01-01-2022 ( Hour) 13
John Doe 01-01-2022 ( Hour) 14
John Doe 01-01-2022 ( Hour) 15
John Doe 01-01-2022 ( Hour) 16
我的开始日期和出院日期都在临时 table 中,我认为我可以使用 CTE 来完成这项工作,但不确定如何 link 我的 table。
如何按小时获取流量明细,以便根据开始日期和出院日期计算每小时有多少人在设施中?
DECLARE @minDateTime AS DATETIME;
DECLARE @maxDateTime AS DATETIME;
SET @minDateTime = '2022-05-01 05:28:05.000';
SET @maxDateTime = '2022-05-02 06:50:00.000';
;
WITH Dates_CTE
AS
(SELECT @minDateTime AS Dates
UNION ALL
SELECT Dateadd(hh, 1, Dates)
FROM Dates_CTE
WHERE Dates < Dateadd(hh, -1, @maxDateTime)
)
SELECT --Convert(VARCHAR,Year,Dates)
Dates
,Year(Dates) as 'Year'
,Month(Dates) as 'Month'
,Day(Dates) as 'day'
,Datename(DW,Dates) as 'DayName'
,DATEPART(HOUR,Dates) as 'hh'
FROM Dates_CTE
OPTION (MAXRECURSION 0)
示例数据
AccountNumber ServiceDateTime RegistrationTypeDischargeDateTime
G111 2021-05-07 10:44:19.000 2021-05-07 14:30:00.000
G222 2021-05-08 09:59:00.000 2021-05-08 10:56:00.000
G333 2021-07-02 11:35:07.000 2021-07-02 11:53:00.000
G444 2021-07-07 07:57:16.000 2021-07-07 13:35:00.000
如果我们有另一个 table 中每个患者的进入和离开日期戳,我们可以加入您的日历 table 并按小时分组以查找在场患者的 ID 并计算它们。
create table inTreatment(
patientid int,
enter datetime,
leave datetime
);
insert into inTreatment values
(1,'2022-05-01 09:00:00','2022-05-01 18:00:00'),
(2,'2022-05-01 11:00:00','2022-05-01 14:00:00'),
(3,'2022-05-01 12:00:00','2022-05-02 15:00:00')
GO
3 行受影响
DECLARE @minDateTime AS DATETIME;
DECLARE @maxDateTime AS DATETIME;
SET @minDateTime = '2022-05-01 05:00:00.000';
SET @maxDateTime = '2022-05-02 06:00:00.000';
;
WITH Dates_CTE
AS
(SELECT @minDateTime AS Dates
UNION ALL
SELECT Dateadd(hh, 1, Dates)
FROM Dates_CTE
WHERE Dates < Dateadd(hh, -1, @maxDateTime)
)
SELECT --Convert(VARCHAR,Year,Dates)
string_agg(patientid,',') patients,
count(patientid) no_pats,
Dates
--,Year(Dates) as 'Year'
--,Month(Dates) as 'Month'
--,Day(Dates) as 'day'
----,Datename(DW,Dates) as 'DayName'
--,DATEPART(HOUR,Dates) as 'hh'
FROM Dates_CTE d
left join InTreatment i
on enter <= Dates and leave >= Dates
group by dates
OPTION (MAXRECURSION 0)
GO
patients | no_pats | Dates
:------- | ------: | :----------------------
null | 0 | 2022-05-01 05:00:00.000
null | 0 | 2022-05-01 06:00:00.000
null | 0 | 2022-05-01 07:00:00.000
null | 0 | 2022-05-01 08:00:00.000
1 | 1 | 2022-05-01 09:00:00.000
1 | 1 | 2022-05-01 10:00:00.000
1,2 | 2 | 2022-05-01 11:00:00.000
1,2,3 | 3 | 2022-05-01 12:00:00.000
1,2,3 | 3 | 2022-05-01 13:00:00.000
1,2,3 | 3 | 2022-05-01 14:00:00.000
1,3 | 2 | 2022-05-01 15:00:00.000
1,3 | 2 | 2022-05-01 16:00:00.000
1,3 | 2 | 2022-05-01 17:00:00.000
1,3 | 2 | 2022-05-01 18:00:00.000
3 | 1 | 2022-05-01 19:00:00.000
3 | 1 | 2022-05-01 20:00:00.000
3 | 1 | 2022-05-01 21:00:00.000
3 | 1 | 2022-05-01 22:00:00.000
3 | 1 | 2022-05-01 23:00:00.000
3 | 1 | 2022-05-02 00:00:00.000
3 | 1 | 2022-05-02 01:00:00.000
3 | 1 | 2022-05-02 02:00:00.000
3 | 1 | 2022-05-02 03:00:00.000
3 | 1 | 2022-05-02 04:00:00.000
3 | 1 | 2022-05-02 05:00:00.000
db<>fiddle here
鉴于此 table 和示例数据:
CREATE TABLE dbo.Admissions
(
AccountNumber char(4),
ServiceDateTime datetime,
RegistrationTypeDischargeDateTime datetime
);
INSERT dbo.Admissions VALUES
('G111','20210507 10:44:19','20210507 14:30:00');
我会这样做:
DECLARE @min datetime = '20210507 05:28:05',
@max datetime = '20210508 06:50:00';
DECLARE @d tinyint = DATEDIFF(HOUR, @min, @max),
@floor datetime = SMALLDATETIMEFROMPARTS
(YEAR(@min), MONTH(@min), DAY(@min), DATEPART(HOUR, @min), 0);
; -- see sqlblog.org/cte
WITH hours(h) AS
(
SELECT @floor UNION ALL
SELECT DATEADD(HOUR, 1, h)
FROM hours WHERE h <= @max
)
SELECT a.AccountNumber, Date = CONVERT(date, hours.h),
Hour = DATEPART(HOUR, hours.h)
FROM hours INNER JOIN dbo.Admissions AS a
ON a.ServiceDateTime < DATEADD(HOUR, 1, hours.h)
AND a.RegistrationTypeDischargeDateTime >= hours.h
OPTION (MAXRECURSION 32767);
输出:
AccountNumber
Date
Hour
G111
2021-05-07
10
G111
2021-05-07
11
G111
2021-05-07
12
G111
2021-05-07
13
G111
2021-05-07
14
您可能需要调整<=/>=/>,具体取决于您希望如何处理极端情况(例如,准时进入或退出,或进入和退出时间 < 1 小时)。
对于快速、简单的方法(与 CTE 相比),使用数字 table 或计数函数交叉应用。在这种情况下,我使用 dbo.fnTally
select a.AccountNumber, cast(a.ServiceDateTime as date) [Date],
datepart(hour, dateadd(hour, fn.N, cast(a.ServiceDateTime as time))) hr
from #Admissions a
cross apply dbo.fnTally(0, datediff(hour,
a.ServiceDateTime,
a.RegistrationTypeDischargeDateTime)) fn;
dbo.fnTally
CREATE FUNCTION [dbo].[fnTally]
/**********************************************************************************************************************
Jeff Moden Script on SSC: https://www.sqlservercentral.com/scripts/create-a-tally-function-fntally
**********************************************************************************************************************/
(@ZeroOrOne BIT, @MaxN BIGINT)
RETURNS TABLE WITH SCHEMABINDING AS
RETURN WITH
H2(N) AS ( SELECT 1
FROM (VALUES
(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
)V(N)) --16^2 or 256 rows
, H4(N) AS (SELECT 1 FROM H2 a, H2 b) --16^4 or 65,536 rows
, H8(N) AS (SELECT 1 FROM H4 a, H4 b) --16^8 or 4,294,967,296 rows
SELECT N = 0 WHERE @ZeroOrOne = 0 UNION ALL
SELECT TOP(@MaxN)
N = ROW_NUMBER() OVER (ORDER BY N)
FROM H8
;
我想根据他们注册的开始日期和出院日期,按星期几和按小时查看当前患者数量。 例如:John doe 开始日期:01-01-2022 13:00:00 ;结束日期 01-01-2022 16:25:00
我希望数据显示 John doe 在设施中的每个小时。所以输出看起来像这样:
John Doe 01-01-2022 ( Hour) 13
John Doe 01-01-2022 ( Hour) 14
John Doe 01-01-2022 ( Hour) 15
John Doe 01-01-2022 ( Hour) 16
我的开始日期和出院日期都在临时 table 中,我认为我可以使用 CTE 来完成这项工作,但不确定如何 link 我的 table。 如何按小时获取流量明细,以便根据开始日期和出院日期计算每小时有多少人在设施中?
DECLARE @minDateTime AS DATETIME;
DECLARE @maxDateTime AS DATETIME;
SET @minDateTime = '2022-05-01 05:28:05.000';
SET @maxDateTime = '2022-05-02 06:50:00.000';
;
WITH Dates_CTE
AS
(SELECT @minDateTime AS Dates
UNION ALL
SELECT Dateadd(hh, 1, Dates)
FROM Dates_CTE
WHERE Dates < Dateadd(hh, -1, @maxDateTime)
)
SELECT --Convert(VARCHAR,Year,Dates)
Dates
,Year(Dates) as 'Year'
,Month(Dates) as 'Month'
,Day(Dates) as 'day'
,Datename(DW,Dates) as 'DayName'
,DATEPART(HOUR,Dates) as 'hh'
FROM Dates_CTE
OPTION (MAXRECURSION 0)
示例数据
AccountNumber ServiceDateTime RegistrationTypeDischargeDateTime
G111 2021-05-07 10:44:19.000 2021-05-07 14:30:00.000
G222 2021-05-08 09:59:00.000 2021-05-08 10:56:00.000
G333 2021-07-02 11:35:07.000 2021-07-02 11:53:00.000
G444 2021-07-07 07:57:16.000 2021-07-07 13:35:00.000
如果我们有另一个 table 中每个患者的进入和离开日期戳,我们可以加入您的日历 table 并按小时分组以查找在场患者的 ID 并计算它们。
create table inTreatment( patientid int, enter datetime, leave datetime ); insert into inTreatment values (1,'2022-05-01 09:00:00','2022-05-01 18:00:00'), (2,'2022-05-01 11:00:00','2022-05-01 14:00:00'), (3,'2022-05-01 12:00:00','2022-05-02 15:00:00') GO
3 行受影响
DECLARE @minDateTime AS DATETIME; DECLARE @maxDateTime AS DATETIME; SET @minDateTime = '2022-05-01 05:00:00.000'; SET @maxDateTime = '2022-05-02 06:00:00.000'; ; WITH Dates_CTE AS (SELECT @minDateTime AS Dates UNION ALL SELECT Dateadd(hh, 1, Dates) FROM Dates_CTE WHERE Dates < Dateadd(hh, -1, @maxDateTime) ) SELECT --Convert(VARCHAR,Year,Dates) string_agg(patientid,',') patients, count(patientid) no_pats, Dates --,Year(Dates) as 'Year' --,Month(Dates) as 'Month' --,Day(Dates) as 'day' ----,Datename(DW,Dates) as 'DayName' --,DATEPART(HOUR,Dates) as 'hh' FROM Dates_CTE d left join InTreatment i on enter <= Dates and leave >= Dates group by dates OPTION (MAXRECURSION 0) GO
patients | no_pats | Dates :------- | ------: | :---------------------- null | 0 | 2022-05-01 05:00:00.000 null | 0 | 2022-05-01 06:00:00.000 null | 0 | 2022-05-01 07:00:00.000 null | 0 | 2022-05-01 08:00:00.000 1 | 1 | 2022-05-01 09:00:00.000 1 | 1 | 2022-05-01 10:00:00.000 1,2 | 2 | 2022-05-01 11:00:00.000 1,2,3 | 3 | 2022-05-01 12:00:00.000 1,2,3 | 3 | 2022-05-01 13:00:00.000 1,2,3 | 3 | 2022-05-01 14:00:00.000 1,3 | 2 | 2022-05-01 15:00:00.000 1,3 | 2 | 2022-05-01 16:00:00.000 1,3 | 2 | 2022-05-01 17:00:00.000 1,3 | 2 | 2022-05-01 18:00:00.000 3 | 1 | 2022-05-01 19:00:00.000 3 | 1 | 2022-05-01 20:00:00.000 3 | 1 | 2022-05-01 21:00:00.000 3 | 1 | 2022-05-01 22:00:00.000 3 | 1 | 2022-05-01 23:00:00.000 3 | 1 | 2022-05-02 00:00:00.000 3 | 1 | 2022-05-02 01:00:00.000 3 | 1 | 2022-05-02 02:00:00.000 3 | 1 | 2022-05-02 03:00:00.000 3 | 1 | 2022-05-02 04:00:00.000 3 | 1 | 2022-05-02 05:00:00.000
db<>fiddle here
鉴于此 table 和示例数据:
CREATE TABLE dbo.Admissions
(
AccountNumber char(4),
ServiceDateTime datetime,
RegistrationTypeDischargeDateTime datetime
);
INSERT dbo.Admissions VALUES
('G111','20210507 10:44:19','20210507 14:30:00');
我会这样做:
DECLARE @min datetime = '20210507 05:28:05',
@max datetime = '20210508 06:50:00';
DECLARE @d tinyint = DATEDIFF(HOUR, @min, @max),
@floor datetime = SMALLDATETIMEFROMPARTS
(YEAR(@min), MONTH(@min), DAY(@min), DATEPART(HOUR, @min), 0);
; -- see sqlblog.org/cte
WITH hours(h) AS
(
SELECT @floor UNION ALL
SELECT DATEADD(HOUR, 1, h)
FROM hours WHERE h <= @max
)
SELECT a.AccountNumber, Date = CONVERT(date, hours.h),
Hour = DATEPART(HOUR, hours.h)
FROM hours INNER JOIN dbo.Admissions AS a
ON a.ServiceDateTime < DATEADD(HOUR, 1, hours.h)
AND a.RegistrationTypeDischargeDateTime >= hours.h
OPTION (MAXRECURSION 32767);
输出:
AccountNumber | Date | Hour |
---|---|---|
G111 | 2021-05-07 | 10 |
G111 | 2021-05-07 | 11 |
G111 | 2021-05-07 | 12 |
G111 | 2021-05-07 | 13 |
G111 | 2021-05-07 | 14 |
您可能需要调整<=/>=/>,具体取决于您希望如何处理极端情况(例如,准时进入或退出,或进入和退出时间 < 1 小时)。
对于快速、简单的方法(与 CTE 相比),使用数字 table 或计数函数交叉应用。在这种情况下,我使用 dbo.fnTally
select a.AccountNumber, cast(a.ServiceDateTime as date) [Date],
datepart(hour, dateadd(hour, fn.N, cast(a.ServiceDateTime as time))) hr
from #Admissions a
cross apply dbo.fnTally(0, datediff(hour,
a.ServiceDateTime,
a.RegistrationTypeDischargeDateTime)) fn;
dbo.fnTally
CREATE FUNCTION [dbo].[fnTally]
/**********************************************************************************************************************
Jeff Moden Script on SSC: https://www.sqlservercentral.com/scripts/create-a-tally-function-fntally
**********************************************************************************************************************/
(@ZeroOrOne BIT, @MaxN BIGINT)
RETURNS TABLE WITH SCHEMABINDING AS
RETURN WITH
H2(N) AS ( SELECT 1
FROM (VALUES
(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
)V(N)) --16^2 or 256 rows
, H4(N) AS (SELECT 1 FROM H2 a, H2 b) --16^4 or 65,536 rows
, H8(N) AS (SELECT 1 FROM H4 a, H4 b) --16^8 or 4,294,967,296 rows
SELECT N = 0 WHERE @ZeroOrOne = 0 UNION ALL
SELECT TOP(@MaxN)
N = ROW_NUMBER() OVER (ORDER BY N)
FROM H8
;