Reputation: 23
I have a table where each line item contains a unit#, date stamp, and bed count. A record is created each day for each unit with the number of beds.
Unit DateTime Beds
----------------------
ICU 2011-03-23 12
ICU 2011-03-24 24
ICU 2011-03-25 24
ICU 2011-03-26 35
ICU 2011-03-27 24
ICU 2011-03-28 24
I am attempting to take the data and create a table like the one below.
Unit Beds StartDate EndDate
------------------------------
ICU 12 2011-03-23 2011-03-23
ICU 24 2011-03-24 2011-03-25
ICU 35 2011-03-26 2011-03-26
ICU 24 2011-03-27 2011-03-28
The issue is that the rows with 24 beds are being combined to get these results.
Unit Beds StartDate EndDate
------------------------------
ICU 12 2011-03-23 2011-03-23
ICU 24 2011-03-24 2011-03-28
ICU 35 2011-03-26 2011-03-26
I tried using DENSE_RANK to assign a ranking to use as a grouping number to separate the instances of the 24 beds. I want to the grouper values to be 1,2,2,3,4,4. Instead the grouper values are 1,2,2,3,2,2.
SELECT DENSE_RANK() OVER(PARTITION BY Unit ORDER BY Beds) AS Grouper,
Unit, DateTime, Beds
FROM StatsLocation
Grouper Unit DateTime Beds
-------------------------------
1 ICU 2011-03-23 12
2 ICU 2011-03-24 24
2 ICU 2011-03-25 24
3 ICU 2011-03-26 35
2 ICU 2011-03-27 24
2 ICU 2011-03-28 24
Upvotes: 2
Views: 726
Reputation: 49260
You can use lag
to check if the previous row has the same value for beds and get the running sum as Grouper column.
SELECT SUM(COL) OVER(PARTITION BY Unit ORDER BY DateTime) as Grouper,Unit,DateTime,Beds
FROM (
SELECT CASE WHEN lag(beds) OVER(PARTITION BY Unit ORDER BY DateTime)=beds then 0 ELSE 1 END AS col,
Unit, DateTime, Beds
FROM StatsLocation
) X
Thereafter, to get the start and end dates is easy with min and max for each group.
WITH CTE AS(
SELECT SUM(COL) OVER(PARTITION BY Unit ORDER BY DateTime) as Grouper,Unit,DateTime,Beds
FROM (SELECT CASE WHEN lag(beds) OVER(PARTITION BY Unit ORDER BY DateTime)=beds then 0 ELSE 1 END AS col,
Unit, DateTime, Beds
FROM StatsLocation) t
)
SELECT UNIT,BEDS,MIN(DATETIME) AS STARTDATE,MAX(DATETIME) AS ENDDATE
FROM CTE
GROUP BY UNIT,BEDS,GROUPER
If you don't need a grouper column,but just the start and end dates, this can be done with difference of row numbers.
SELECT UNIT,BEDS,MIN(DATETIME) AS STARTDATE,MAX(DATETIME) AS ENDDATE
FROM (
SELECT ROW_NUMBER() OVER(PARTITION BY Unit ORDER BY Dt)
- ROW_NUMBER() OVER(PARTITION BY Unit,Beds ORDER BY Dt) AS Grouper,
Unit, Dt, Beds
FROM StatsLocation) T
GROUP BY UNIT,BEDS,GROUPER
Upvotes: 2
Reputation: 38043
This is a gaps and islands problem, you can solve it using two row_number()
s like so:
select
Unit
, Beds
, StartDate = min(DateTime)
, EndDate = max(DateTime)
from (
select *
, rn_x = row_number() over (partition by unit order by [datetime])
, rn_y = row_number() over (partition by unit, beds order by [datetime])
from t
) as s
group by Unit, Beds, rn_x-rn_y
order by Unit, StartDate
rextester demo: http://rextester.com/IJXC7931
returns:
+------+------+------------+------------+
| Unit | Beds | StartDate | EndDate |
+------+------+------------+------------+
| ICU | 12 | 2011-03-23 | 2011-03-23 |
| ICU | 24 | 2011-03-24 | 2011-03-25 |
| ICU | 35 | 2011-03-26 | 2011-03-26 |
| ICU | 24 | 2011-03-27 | 2011-03-28 |
+------+------+------------+------------+
Upvotes: 0