How to find the difference between dates within the same column using SQL? - sql

I am trying to solve the following challenge:
1) If a patient visits the ER within 48 hours, I want to flag that as 1.
2) If the same patient visits the ER again after 48 hours, I want to flag that as 2.
3) Each subsequent visit must be flagged as 3, 4, 5 etcetera after the first 48 hours.
Here is what my table looks like:
PATIENT_ID ADMIT_DATE LOCATION
---------- ---------- --------
33 1/10/2014 ER
33 1/11/2014 ER
33 1/15/2014 ER
33 1/17/2014 ER
45 2/20/2014 OBS
45 2/21/2014 OBS
45 2/25/2014 OBS
45 2/30/2014 OBS
45 2/32/2014 OBS
And here is what the desired result should look like:
PATIENT_ID ADMIT_DATE LOCATION FLAG
---------- ---------- -------- ----
33 1/10/2014 ER 1
33 1/15/2014 ER 2
33 1/17/2014 ER 3
45 2/20/2014 OBS 1
45 2/25/2014 OBS 2
45 2/30/2014 OBS 3
45 2/32/2014 OBS 4
I have started something like this but could not complete it:
SELECT PATIENT_ID, ADMIT_DATE, LOCATION,
CASE WHEN MIN(ADMIT_DATE)-MAX(ADMIT_DATE)<48 THEN 1 ELSE 0 AS FLAG
FROM MYTABLE
GROUP BY PATIENT_ID, ADMIT_DATE, LOCATION
Can someone please help?

You can achieve this easy using LAG, DATEDIFF and ROWNUMBER functions. The LAG function helps you to get the previous ADMIT_DATE value. Then you can calculate the difference in hours using the DATEDIFF function. Finally, using ROWNUMBER you can simple rank your results.
This is full working example:
SET NOCOUNT ON
GO
DECLARE #DataSource TABLE
(
[ATIENT_ID] TINYINT
,[ADMIT_DATE] DATE
,[LOCATION] VARCHAR(3)
)
INSERT INTO #DataSource ([ATIENT_ID], [ADMIT_DATE], [LOCATION])
VALUES (33, '1-10-2014', 'ER')
,(33, '1-11-2014', 'ER')
,(33, '1-15-2014', 'ER')
,(33, '1-17-2014', 'ER')
,(45, '2-15-2014', 'OBS')
,(45, '2-16-2014', 'OBS')
,(45, '2-20-2014', 'OBS')
,(45, '2-25-2014', 'OBS')
,(45, '2-27-2014', 'OBS')
;WITH DataSource ([ATIENT_ID], [ADMIT_DATE], [LOCATION], [DIFF_IN_HOURS]) AS
(
SELECT [ATIENT_ID]
,[ADMIT_DATE]
,[LOCATION]
,DATEDIFF(
HOUR
,LAG([ADMIT_DATE], 1, NULL) OVER (PARTITION BY [ATIENT_ID], [LOCATION] ORDER BY [ADMIT_DATE] ASC)
,[ADMIT_DATE]
)
FROM #DataSource
)
SELECT [ATIENT_ID]
,[ADMIT_DATE]
,[LOCATION]
,ROW_NUMBER() OVER (PARTITION BY [ATIENT_ID], [LOCATION] ORDER BY [ADMIT_DATE] ASC)
FROM DataSource
WHERE [DIFF_IN_HOURS] >= 48
OR [DIFF_IN_HOURS] IS NULL -- these are first records
SET NOCOUNT OFF
GO
Note, I have fixed your sample data as it was wrong.
This is alternative solution without LAG function:
;WITH TempDataSource ([ATIENT_ID], [ADMIT_DATE], [LOCATION], [Rank]) AS
(
SELECT [ATIENT_ID]
,[ADMIT_DATE]
,[LOCATION]
,ROW_NUMBER() OVER (PARTITION BY [ATIENT_ID], [LOCATION] ORDER BY [ADMIT_DATE] ASC)
FROM #DataSource
),
DataSource ([ATIENT_ID], [ADMIT_DATE], [LOCATION], [DIFF_IN_HOURS]) AS
(
SELECT DS1.[ATIENT_ID]
,DS1.[ADMIT_DATE]
,DS1.[LOCATION]
,DATEDIFF(HOUR, DS2.[ADMIT_DATE], DS1.[ADMIT_DATE])
FROM TempDataSource DS1
LEFT JOIN TempDataSource DS2
ON DS1.[Rank] - 1 = DS2.[Rank]
AND DS1.[ATIENT_ID] = DS2.[ATIENT_ID]
AND DS1.[LOCATION] = DS2.[LOCATION]
)
SELECT [ATIENT_ID]
,[ADMIT_DATE]
,[LOCATION]
,ROW_NUMBER() OVER (PARTITION BY [ATIENT_ID], [LOCATION] ORDER BY [ADMIT_DATE] ASC)
FROM DataSource
WHERE [DIFF_IN_HOURS] >= 48
OR [DIFF_IN_HOURS] IS NULL -- these are first records

SELECT Patient_id,Admit_date, Location,
CASE WHEN DATEDIFF (HH , min(admit_date) , max(admit_date)) < 48 THEN count(flag)+1 ELSE 0 End As Flag
FROM tbl_Patient
GROUP BY PATIENT_ID, ADMIT_DATE, LOCATION

you can use DATEDIFF() available in sql-server like
SELECT DATEDIFF(hour,startDate,endDate) AS 'Duration'
You can visit http://msdn.microsoft.com/en-IN/library/ms189794.aspx

Related

Determine if a log entry is the first ever entry for a product

I am trying to determine the first entry for a product in SQL Server. The table is a log of JDE updates with a record status of A when it is added.
Our products are grouped where we have many codes for the same product with different batches. The first 19 chars of the product code will be the same regardless of batch.
Sample data:
Only the bolded row would be the record I want to return as that is the first entry for that First19 code.
This is the SQL I put together (Excuse the mess around the dates, it's what i have to do to make it a true date from how JDE stores dates):
SELECT DATEADD(DAY,CONVERT(INT,RIGHT(F4101Z1.SZUPMJ,3))-1,DATEADD(YEAR,CONVERT(INT,LEFT(F4101Z1.SZUPMJ,3)),'01/01/1900')) Modified_Date,
F4101Z1.SZTNAC Record_Status,
F4101Z1.SZLITM,
LEFT(F4101Z1.SZLITM,19) First19
FROM ODS.PRODDTA.F4101Z1 F4101Z1
LEFT OUTER JOIN (
SELECT LEFT(SZLITM,19) First19
FROM ODS.PRODDTA.F4101Z1
WHERE DATEADD(DAY,CONVERT(INT,RIGHT(SZUPMJ,3))-1,DATEADD(YEAR,CONVERT(INT,LEFT(SZUPMJ,3)),'01/01/1900')) = '11/12/2020'
) F4101Z1_2 ON LEFT(F4101Z1.SZLITM,19) = First19
WHERE F4101Z1_2.First19 IS NULL
AND F4101Z1.SZTNAC = 'A'
The code returns all 3 results which is not what I expect I expected the bolded entry only.
I actually want to put a date criteria on there so I can frame this into a report which I run for the previous day. Basically to show any NEW products that have been created where they are genuine new products and now new batches.
I think you can use row_number();
select f.*
from (select f.*,
row_number() over (partition by first19 order by modified_date asc, SZLITM asc) as seqnum
from ODS.PRODDTA.F4101Z1 f
) f
where seqnum = 1;
If modified_date is really a string and not a date -- well, then you should fix the data model. But you can convert it to a date if need be:
row_number() over (partition by first19 order by convert(date, modified_date, 103) asc, SZLITM asc) as seqnum
It's possible I'm not understanding your need, by my first thought is to do something like this:
DECLARE #Data table (
SZUPMJ varchar(6), SZTNAC varchar(1), SZLITM varchar(50)
);
INSERT INTO #Data ( SZUPMJ, SZTNAC, SZLITM ) VALUES
( '120286', 'A', '280080010460160100150' ),
( '120286', 'A', '280080010460160100151' ),
( '120287', 'A', '280080010460160100150' );
SELECT
DATEADD( DAY, CONVERT( INT, RIGHT( F4101Z1.SZUPMJ, 3 ) ) -1, DATEADD( YEAR, CONVERT( INT, LEFT( F4101Z1.SZUPMJ, 3 ) ), '01/01/1900' ) ) Modified_Date,
F4101Z1.SZTNAC AS Record_Status,
MIN( F4101Z1.SZLITM ) AS Initial_Batch,
LEFT( F4101Z1.SZLITM, 19 ) AS First19
FROM #Data F4101Z1
LEFT OUTER JOIN (
SELECT
LEFT( SZLITM, 19 ) First19
FROM #Data d
WHERE
DATEADD( DAY, CONVERT( INT, RIGHT( SZUPMJ, 3 ) ) -1, DATEADD( YEAR, CONVERT( INT, LEFT( SZUPMJ, 3 ) ), '01/01/1900' ) ) = '11/12/2020'
) F4101Z1_2
ON LEFT( F4101Z1.SZLITM, 19 ) = First19
WHERE
F4101Z1_2.First19 IS NULL
AND F4101Z1.SZTNAC = 'A'
GROUP BY
F4101Z1.SZUPMJ, F4101Z1.SZTNAC, LEFT( F4101Z1.SZLITM, 19 )
ORDER BY
First19;
Returns
+-------------------------+---------------+-----------------------+---------------------+
| Modified_Date | Record_Status | Initial_Batch | First19 |
+-------------------------+---------------+-----------------------+---------------------+
| 2020-10-12 00:00:00.000 | A | 280080010460160100150 | 2800800104601601001 |
| 2020-10-13 00:00:00.000 | A | 280080010460160100150 | 2800800104601601001 |
+-------------------------+---------------+-----------------------+---------------------+
I simply select the MIN value for SZLITM for the specified First19 value. I've grouped by the modified date to show the results for each day, but you can change that easily enough to return one row. I'm not sure what your LEFT OUTER JOIN is doing, but I left it as is.
Maybe you can Distinct on First19 column and if necessary order by Modified Date?
Thanks #Critical Error and #SMor. I have got this working now. Here is the code:
SELECT DATEADD( DAY, CONVERT( INT, RIGHT( F4101Z1.SZUPMJ, 3 ) ) -1, DATEADD( YEAR, CONVERT( INT, LEFT( F4101Z1.SZUPMJ, 3 ) ), '01/01/1900' ) ) Modified_Date,
F4101Z1.SZTNAC AS Record_Status,
MIN( F4101Z1.SZLITM ) AS Initial_Batch,
LEFT( F4101Z1.SZLITM, 19 ) AS First19
FROM ODS.PRODDTA.F4101Z1 F4101Z1
WHERE F4101Z1.SZTNAC = 'A'
AND DATEADD( DAY, CONVERT( INT, RIGHT( F4101Z1.SZUPMJ, 3 ) ) -1, DATEADD( YEAR, CONVERT( INT, LEFT( F4101Z1.SZUPMJ, 3 ) ), '01/01/1900' ) ) = '10/13/2020'
AND (
SELECT count (LEFT(F4101Z1_2.SZLITM, 19 ))
FROM ODS.PRODDTA.F4101Z1 F4101Z1_2
WHERE F4101Z1_2.SZUPMJ < F4101Z1.SZUPMJ
AND LEFT(F4101Z1_2.SZLITM, 19 ) = LEFT(F4101Z1.SZLITM, 19 )
) = 0
GROUP BY
F4101Z1.SZUPMJ,
F4101Z1.SZTNAC,
LEFT( F4101Z1.SZLITM, 19 )
ORDER BY
First19;
This little gem is working out if it previously exists and doesn't show it if there is a count of records above 0:
AND (
SELECT count (LEFT(F4101Z1_2.SZLITM, 19 ))
FROM ODS.PRODDTA.F4101Z1 F4101Z1_2
WHERE F4101Z1_2.SZUPMJ < F4101Z1.SZUPMJ
AND LEFT(F4101Z1_2.SZLITM, 19 ) = LEFT(F4101Z1.SZLITM, 19 )
) = 0
Your comments helped me work this out and get rid of that nasty left outer join

SQL Server: query to get the data between two values from same columns and calculate time difference

I have a requirement to get the number of hours between two values, say 20 and 25 or above (this will be user input values and not fixed). Below is the table with sample data.
Consider in the table on 01-09-2016 08:40 value_ID is 25 and it reaches back to 20 on 02-09-2016 13:20, I need to consider the number of hours between these two range ie 12 hours and 40 min it is .. Similarly 04-09-2016 13:20 it reached 26.3 (which is above 25 ) and '06-09-2016 16:20' reached 19.3 (below 20) and number of hours is 45 hours. I tried creating a function, however it's not working..
CODE TO CREATE TABLE:
CREATE TABLE [dbo].[NumOfHrs](
[ID] [float] NULL,
[Date] [datetime] NULL,
[Value_ID] [float] NULL
) ON [PRIMARY]
CODE to insert data :
INSERT INTO [dbo].[NumOfHrs]
([ID]
,[Date]
,[Value_ID])
VALUES
(112233,'8-31-2016 08:20:00',19.2),
(112233,'9-01-2016 08:30:00',24),
(112233,'9-01-2016 08:40:00',25),
(112233,'9-01-2016 09:20:00',26),
(112233,'9-02-2016 10:20:00',27),
(112233,'9-02-2016 10:20:00',24),
(112233,'9-02-2016 10:20:00',23),
(112233,'9-02-2016 11:20:00',22),
(112233,'9-02-2016 12:20:00',21),
(112233,'9-02-2016 13:20:00',20),
(112233,'9-03-2016 13:20:00',19.8),
(112233,'9-04-2016 13:20:00',21),
(112233,'9-04-2016 14:20:00',24),
(112233,'9-04-2016 16:20:00',24.6),
(112233,'9-04-2016 19:20:00',26.3),
(112233,'9-04-2016 23:20:00',27),
(112233,'9-05-2016 00:20:00',22),
(112233,'9-06-2016 16:20:00',19.3),
(112233,'9-07-2016 00:20:00',22),
(112233,'9-08-2016 00:20:00',21),
(112233,'9-09-2016 00:20:00',23),
(445566,'9-10-2016 00:20:00',24),
(445566,'9-11-2016 00:20:00',25),
(445566,'9-12-2016 00:20:00',26),
(445566,'9-13-2016 00:20:00',24),
(445566,'9-14-2016 00:20:00',23),
(445566,'9-15-2016 00:20:00',24),
(445566,'9-16-2016 00:20:00',21),
(445566,'9-17-2016 00:20:00',20),
(445566,'9-18-2016 00:20:00',18.5),
(445566,'9-19-2016 00:20:00',17)
image of the table:
Well, I couldn't think of anything simpler. Here's my try to solve the problem:
;with NumOfHrs_rn as (
select id, [Date], Value_ID,
row_number() over (partition by id order by [date]) AS rn
from [dbo].[NumOfHrs]
), NumOfHrs_lag as (
select t1.id, t1.[date],
t2.Value_ID as prev_value,
t1.Value_ID as curr_value
from NumOfHrs_rn as t1
-- get previous value (lag)
join NumOfHrs_rn as t2 on t1.id = t2.id and t1.rn = t2.rn + 1
), NumOfHrs_flag as (
select id, [Date], prev_value, curr_value,
case
when curr_value >= 25 and prev_value < 25 then 'start'
when curr_value <= 20 and prev_value > 20 then 'stop'
else 'ignore'
end as flag
from NumOfHrs_lag
), NumOfHrs_grp as (
select id, [Date], curr_value, flag,
row_number() over (partition by id order by [Date]) -
case flag
when 'start' then 0
when 'stop' then 1
end as grp
from NumOfHrs_flag
where flag in ('start', 'stop')
)
select min([Date]) AS 'start', max([Date]) as 'stop'
from NumOfHrs_grp
group by id, grp
order by min([Date])
Output:
start stop
------------------------------------------------
2016-09-01 08:40:00.000 2016-09-02 13:20:00.000
2016-09-04 19:20:00.000 2016-09-06 16:20:00.000
2016-09-11 00:20:00.000 2016-09-17 00:20:00.000
You can manipulate the above query in order to get the time difference expressed in hours/minutes/seconds format.
Demo here

SQL Server : aging based on last date

RN LSD ED Aging
------------------------------------------
11111111 09-Feb-2017 06-Feb-2017 3
22222222 09-Feb-2017 02-Feb-2017 7
33333333 20-Jan-2017 29-Nov-2016 44
33333333 20-Jan-2017 07-Dec-2016 44
33333333 20-Jan-2017 29-Nov-2016 44
33333333 20-Jan-2017 30-Nov-2016 44
33333333 20-Jan-2017 29-Nov-2016 44
44444444 12-Dec-2016 19-Nov-2016 17
44444444 12-Dec-2016 19-Nov-2016 17
44444444 12-Dec-2016 25-Nov-2016 17
55555555 07-Feb-2017 04-Dec-2016 56
55555555 07-Feb-2017 04-Dec-2016 56
55555555 07-Feb-2017 13-Dec-2016 56
How can we update aging column based on last (max) ED Date column by grouping by RN column?
I think the best way to achieve your goal is something like bellow
DECLARE #MyTable TABLE
(
RN int,
LSD date,
ED date,
Aging int
)
INSERT INTO #MyTable VALUES
('11111111' ,'09-Feb-2017' ,'06-Feb-2017' ,'3'),
('22222222' ,'09-Feb-2017' ,'02-Feb-2017' ,'7' ),
('33333333' ,'20-Jan-2017' ,'29-Nov-2016' ,'44'),
('33333333' ,'20-Jan-2017' ,'07-Dec-2016' ,'44'),
('33333333' ,'20-Jan-2017' ,'29-Nov-2016' ,'44'),
('33333333' ,'20-Jan-2017' ,'30-Nov-2016' ,'44') ,
('33333333' ,'20-Jan-2017' ,'29-Nov-2016' ,'44'),
('44444444' ,'12-Dec-2016' ,'19-Nov-2016' ,'17') ,
('44444444' ,'12-Dec-2016' ,'19-Nov-2016' ,'17'),
('44444444' ,'12-Dec-2016' ,'25-Nov-2016' ,'17'),
('55555555' ,'07-Feb-2017' ,'04-Dec-2016' ,'56') ,
('55555555' ,'07-Feb-2017' ,'04-Dec-2016' ,'56'),
('55555555' ,'07-Feb-2017' ,'13-Dec-2016' ,'56')
;with cteToUpdate AS
(
SELECT RN,DATEDIFF(D,MAX(ED),MIN(LSD)) as Aging FROM #MyTable
GROUP BY RN
)
UPDATE source
SET source.Aging = cteToUpdate.Aging
FROM #MyTable as source
JOIN cteToUpdate ON cteToUpdate.RN =source.RN
I would recommend changing date format to yyyy-mm-dd as it makes easier in calculation and sorting. Then you should be able to work out the difference with SQL query.
Syntax
DATEDIFF(datepart, startdate, enddate)
Example
SELECT DATEDIFF(day, '2014-08-05', '2014-06-05') AS DiffDate
You Can Try This I guess. But I am not Clear with your requirement. The code you find below is not the shortest of the solutions, but will do the job.
SELECT X.RN , X.LSD, X.ED , B.Aging
FROM X
LEFT JOIN
(
SELECT RN , DATEDIFF(day,LSD,ED) Aging
FROM
(
SELECT RN , MIN(LSD) LSD, MAX(ED) ED
FROM X
GROUP BY RN
)A
)B
ON X.RN = B.RN
Your update will look like this:
update tab as t1
set aging =
(select (DATEDIFF(day, MIN(startdate), MAX(enddate))
from tab as t2
where t1.rn = t2.rn
)
But you might do this dynamically in a view using
DATEDIFF(day, MIN(startdate) OVER (PARTITION BY RN),
MAX(enddate) OVER (PARTITION BY RN)) as Aging
You can use max and partition by along with CTE
;with cte as (
select *, DateDiff(day, ED, LSD) as Ag, Max(Ed) over (partition by rn) MaxEd from #yourAging
) Select a.RN, a.LSD, a.ED, Aging = sum(tmpSm) over(partition by a.rn)
from ( select *,case when max(ed) over (partition by rn)=ED then Ag else 0 end as TmpSm from cte ) a
Your Code demo with input tables

How to find the time difference between data present in two rows

I have the following table :
Customer_ID PurchaseDatetime
309 2/3/2014 12:29:00
309 2/27/2014 17:11:00
309 4/15/2014 13:24:00
I want to write a query which would calculate the difference between the datetime field of two consecutive rows. Ideally the output should be like
Customer_ID PurchaseDatetime
309 0
309 2/27/2014 17:11:00 - 2/3/2014 12:29:00 // The exact time difference in hours
309 4/15/2014 13:24:00 - 2/27/2014 17:11:00 // The exact time difference in hours
How do I write such a query?
Try this...
CREATE TABLE #Purchases
(
CustomerID INT,
PurchaseDate DATETIME
)
INSERT INTO #Purchases
VALUES
(100004,'2016-05-16 08:00:00'),
(100005,'2016-05-16 09:05:00'),
(100006,'2016-05-16 10:08:40'),
(32141 ,'2016-05-16 11:18:00'),
(84230 ,'2016-05-16 12:25:10'),
(23444 ,'2016-05-16 13:40:00'),
(100001,'2016-05-16 14:50:00')
;WITH CTE AS
(
SELECT
CustomerID,
PurchaseDate,
ROW_NUMBER() OVER (ORDER BY PurchaseDate) AS Seq
FROM #Purchases
)
SELECT
p.CustomerID,
p.PurchaseDate,
pl.PurchaseDate,
DATEADD(SECOND,DATEDIFF(SECOND, pl.PurchaseDate,p.PurchaseDate),0) AS DiffDT,
DATEDIFF(HOUR, pl.PurchaseDate,p.PurchaseDate) HourDiff
FROM CTE AS p
LEFT OUTER JOIN CTE AS pl ON pl.Seq = p.Seq - 1 -- Last batch
ORDER BY p.PurchaseDate
Try This Query...
;with cte as
(select row_number() over(order by (select 100)) Id.Customer_ID,PurchaseDatetime from Table)
select a.Customer_ID,b.PurchaseDatetime-a.PurchaseDatetime from cte a inner join cte b on a.id=b.id-1
Thanks
SELECT *,
PURCHASEDATETIME =
CASE CUSTOMER_ID
WHEN CUSTOMER_ID THEN
DATEDIFF(HH, LAG(PURCHASEDATETIME, 1) OVER(ORDER BY CUSTOMER_ID, PURCHASEDATETIME), PURCHASEDATETIME)
ELSE
NULL
END
FROM table

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