简体   繁体   中英

How to count open records, grouped by hour and day in SQL-server-2008-r2

I have hospital patient admission data in Microsoft SQL Server r2 that looks something like this:

PatientID, AdmitDate,        DischargeDate
Jones.     1-jan-13 01:37.   1-jan-13 17:45
Smith      1-jan-13 02:12.   2-jan-13 02:14
Brooks.    4-jan-13 13:54.   5-jan-13 06:14

I would like count the number of patients in the hospital day by day and hour by hour (ie at

1-jan-13 00:00. 0
1-jan-13 01:00. 0
1-jan-13 02:00. 1
1-jan-13 03:00. 2

And I need to include the hours when there are no patients admitted in the result.

I can't create tables so making a reference table listing all the hours and days is out, though.

Any suggestions?

Here is one (ugly) way:

;WITH DayHours AS
(
    SELECT 0 DayHour
    UNION ALL
    SELECT DayHour+1
    FROM DayHours
    WHERE DayHour+1 <= 23
)
SELECT B.AdmitDate, A.DayHour, COUNT(DISTINCT PatientID) Patients
FROM DayHours A
CROSS JOIN (SELECT DISTINCT CONVERT(DATE,AdmitDate) AdmitDate
            FROM YourTable) B
LEFT JOIN YourTable C
    ON B.AdmitDate = CONVERT(DATE,C.AdmitDate)
    AND A.DayHour = DATEPART(HOUR,C.AdmitDate)
GROUP BY B.AdmitDate, A.DayHour

To solve this problem, you need a list of date-hours. The following gets this from the admit date cross joined to a table with 24 hours. The table of 24 hours is calculating from information_schema.columns -- a trick for getting small sequences of numbers in SQL Server.

The rest is just a join between this table and the hours. This version counts the patients at the hour, so someone admitted and discharged in the same hour, for instance is not counted. And in general someone is not counted until the next hour after they are admitted:

with dh as (
     select DATEADD(hour, seqnum - 1, thedatehour ) as DateHour
     from (select distinct cast(cast(AdmitDate as DATE) as datetime) as thedatehour
           from Admission a
          ) a cross join
          (select ROW_NUMBER() over (order by (select NULL)) as seqnum
           from INFORMATION_SCHEMA.COLUMNS
          ) hours
          where hours <= 24
    )
select dh.DateHour, COUNT(*) as NumPatients
from dh join
     Admissions a
     on dh.DateHour between a.AdmitDate and a.DischargeDate
group by dh.DateHour
order by 1

This also assumes that there are admissions on every day. That seems like a reasonable assumption. If not, a calendar table would be a big help.

This is a bit messy and includes a temp table with the test data you provided but

    CREATE TABLE #HospitalPatientData (PatientId NVARCHAR(MAX), AdmitDate DATETIME, DischargeDate DATETIME)
INSERT INTO #HospitalPatientData
SELECT 'Jones.',     '1-jan-13 01:37:00.000',   '1-jan-13 17:45:00.000' UNION
SELECT 'Smith',      '1-jan-13 02:12:00.000',   '2-jan-13 02:14:00.000' UNION
SELECT 'Brooks.',    '4-jan-13 13:54:00.000',  '5-jan-13 06:14:00.000'

;WITH DayHours AS
(
    SELECT 0 DayHour
    UNION ALL
    SELECT DayHour+1
    FROM DayHours
    WHERE DayHour+1 <= 23
),
HospitalPatientData AS
(
SELECT CONVERT(nvarchar(max),AdmitDate,103) as AdmitDate ,DATEPART(hour,(AdmitDate)) as     AdmitHour, COUNT(PatientID) as CountOfPatients
FROM #HospitalPatientData
GROUP BY CONVERT(nvarchar(max),AdmitDate,103), DATEPART(hour,(AdmitDate))
),
Results AS
(
SELECT MAX(h.AdmitDate) as Date, d.DayHour
FROM HospitalPatientData h
INNER JOIN DayHours d ON d.DayHour=d.DayHour
GROUP BY AdmitDate, CountOfPatients, DayHour
)

SELECT r.*, COUNT(h.PatientId) as CountOfPatients
FROM Results r
LEFT JOIN #HospitalPatientData h ON CONVERT(nvarchar(max),AdmitDate,103)=r.Date AND     DATEPART(HOUR,h.AdmitDate)=r.DayHour
GROUP BY r.Date, r.DayHour
ORDER BY r.Date, r.DayHour

DROP TABLE #HospitalPatientData

This may get you started:

BEGIN TRAN

DECLARE @pt TABLE
    (
      PatientID VARCHAR(10)
    , AdmitDate DATETIME
    , DischargeDate DATETIME
    )

INSERT  INTO @pt
        ( PatientID, AdmitDate, DischargeDate )
VALUES  ( 'Jones', '1-jan-13 01:37', '1-jan-13 17:45' ),
        ( 'Smith', '1-jan-13 02:12', '2-jan-13 02:14' )
,       ( 'Brooks', '4-jan-13 13:54', '5-jan-13 06:14' )



DECLARE @StartDate DATETIME = '20130101'
    , @FutureDays INT = 7


;
WITH    dy
          AS ( SELECT TOP (@FutureDays)
                        ROW_NUMBER() OVER ( ORDER BY name ) dy
               FROM     sys.columns c
             ) ,
        hr
          AS ( SELECT TOP 24
                        ROW_NUMBER() OVER ( ORDER BY name ) hr
               FROM     sys.columns c
             )
    SELECT  refDate, COUNT(p.PatientID) AS PtCount
    FROM    ( SELECT    DATEADD(HOUR, hr.hr - 1,
                                DATEADD(DAY, dy.dy - 1, @StartDate)) AS refDate
              FROM      dy
                        CROSS JOIN hr
            ) ref
    LEFT JOIN @pt p ON ref.refDate BETWEEN p.AdmitDate AND p.DischargeDate        
    GROUP BY refDate

ORDER BY refDate

ROLLBACK

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM