patternsqlMinor
Find total duration of each consecutive series of rows
Viewed 0 times
totalrowseachdurationfindseriesconsecutive
Problem
MySQL Version
The code will run in MySQL 5.5
Background
I have a table like the following one
This table is about patients in a hospital and it stores the beds where each patient spent some time while being hospitalized.
Each ward may have multiple beds and each patient may move to a different bed within the same ward.
Objective
What I want to do is to find how much time each patient spent in a specific ward without having moved to a different ward. I.e I want to find the total duration of the consecutive time he spent within the same ward.
Test case
```
-- Let's assume that ward_id = 1 corresponds to ICU (Intensive Care Unit)
INSERT INTO t
(patient_id, bed_id, ward_id, admitted, discharged)
VALUES
-- Patient 1 is in ICU, changes some beds, then he is moved
-- out of ICU, back in and finally he is out.
(1, 1, 1, '2015-01-06 06:05:00', '2015-01-07 06:04:00'),
(1, 2, 1, '2015-01-07 06:04:00', '2015-01-07 07:08:00'),
(1, 1, 1, '2015-01-07 07:08:00', '2015-01-08 08:11:00'),
(1, 4, 2, '2015-01-08 08:11:00', '2015-01-08 09:11:00'),
(1, 1, 1, '2015-01-08 09:11:00', '2015-01-08 10:11:00'),
(1, 3, 1, '2015-01-08 10:11:00', '2015-01-08 11:11:00'),
(1, 1, 2, '2015-01-08 11:11:00', '2015-01-08 12:11:00'),
-- Patient 2 is out of ICU, he gets inserted in ICU,
-- changes some beds and he is back out
(2, 1, 2, '2015-01-06 06:00:00', '2015-01-07 06:04:00'),
(2, 1, 1, '2015-01-07 06:04:00', '2015-01-07 07:08:00'),
(2, 3, 1, '2015-01-07 07:08:00', '2015-01-08 08:11:00'),
(2, 1, 2, '2015-01-08 08:11:00', '2015-01-08 09:11:00'),
-- Patient 3 is not inserted in ICU
(3, 1, 2, '2015-01-08 08:10:00', '2015-01-09 09:00:00'),
(3, 2, 2, '2015-01-09 09:00:00', '2015-01-10 10:01:00'),
(3, 3, 2, '2015-01-10 10:01:00', '2015-01-11 12
The code will run in MySQL 5.5
Background
I have a table like the following one
CREATE TABLE t
( id INT NOT NULL AUTO_INCREMENT
, patient_id INT NOT NULL
, bed_id INT NOT NULL
, ward_id INT NOT NULL
, admitted DATETIME NOT NULL
, discharged DATETIME
, PRIMARY KEY (id)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;This table is about patients in a hospital and it stores the beds where each patient spent some time while being hospitalized.
Each ward may have multiple beds and each patient may move to a different bed within the same ward.
Objective
What I want to do is to find how much time each patient spent in a specific ward without having moved to a different ward. I.e I want to find the total duration of the consecutive time he spent within the same ward.
Test case
```
-- Let's assume that ward_id = 1 corresponds to ICU (Intensive Care Unit)
INSERT INTO t
(patient_id, bed_id, ward_id, admitted, discharged)
VALUES
-- Patient 1 is in ICU, changes some beds, then he is moved
-- out of ICU, back in and finally he is out.
(1, 1, 1, '2015-01-06 06:05:00', '2015-01-07 06:04:00'),
(1, 2, 1, '2015-01-07 06:04:00', '2015-01-07 07:08:00'),
(1, 1, 1, '2015-01-07 07:08:00', '2015-01-08 08:11:00'),
(1, 4, 2, '2015-01-08 08:11:00', '2015-01-08 09:11:00'),
(1, 1, 1, '2015-01-08 09:11:00', '2015-01-08 10:11:00'),
(1, 3, 1, '2015-01-08 10:11:00', '2015-01-08 11:11:00'),
(1, 1, 2, '2015-01-08 11:11:00', '2015-01-08 12:11:00'),
-- Patient 2 is out of ICU, he gets inserted in ICU,
-- changes some beds and he is back out
(2, 1, 2, '2015-01-06 06:00:00', '2015-01-07 06:04:00'),
(2, 1, 1, '2015-01-07 06:04:00', '2015-01-07 07:08:00'),
(2, 3, 1, '2015-01-07 07:08:00', '2015-01-08 08:11:00'),
(2, 1, 2, '2015-01-08 08:11:00', '2015-01-08 09:11:00'),
-- Patient 3 is not inserted in ICU
(3, 1, 2, '2015-01-08 08:10:00', '2015-01-09 09:00:00'),
(3, 2, 2, '2015-01-09 09:00:00', '2015-01-10 10:01:00'),
(3, 3, 2, '2015-01-10 10:01:00', '2015-01-11 12
Solution
Query 1, tested in SQLFiddle-1
Query 2, which is the same as 1 but without the derived tables. This will probably have better execution plan, with proper indexes. Test in SQLFiddle-2:
Both queries assume that there is a unique constraint on
SET @ward_id_to_check = 1 ;
SELECT
st.patient_id,
st.bed_id AS starting_bed_id, -- the first bed a patient uses
-- can be omitted
st.admitted,
MIN(en.discharged) AS discharged
FROM
( SELECT patient_id, bed_id, admitted, discharged
FROM t
WHERE t.ward_id = @ward_id_to_check
AND NOT EXISTS
( SELECT *
FROM t AS prev
WHERE prev.ward_id = @ward_id_to_check
AND prev.patient_id = t.patient_id
AND prev.discharged = t.admitted
)
) AS st
JOIN
( SELECT patient_id, admitted, discharged
FROM t
WHERE t.ward_id = @ward_id_to_check
AND NOT EXISTS
( SELECT *
FROM t AS next
WHERE next.ward_id = @ward_id_to_check
AND next.patient_id = t.patient_id
AND next.admitted = t.discharged
)
) AS en
ON st.patient_id = en.patient_id
AND st.admitted <= en.admitted
GROUP BY
st.patient_id,
st.admitted ;Query 2, which is the same as 1 but without the derived tables. This will probably have better execution plan, with proper indexes. Test in SQLFiddle-2:
SET @ward_id_to_check = 1 ;
SELECT
st.patient_id,
st.bed_id AS starting_bed_id,
st.admitted,
MIN(en.discharged) AS discharged
FROM
t AS st -- starting period
JOIN
t AS en -- ending period
ON en.ward_id = @ward_id_to_check
AND st.patient_id = en.patient_id
AND NOT EXISTS
( SELECT *
FROM t AS next
WHERE next.ward_id = @ward_id_to_check
AND next.patient_id = en.patient_id
AND next.admitted = en.discharged
)
AND st.admitted <= en.admitted
WHERE
st.ward_id = @ward_id_to_check
AND NOT EXISTS
( SELECT *
FROM t AS prev
WHERE prev.ward_id = @ward_id_to_check
AND prev.patient_id = st.patient_id
AND prev.discharged = st.admitted
)
GROUP BY
st.patient_id,
st.admitted ;Both queries assume that there is a unique constraint on
(patient_id, admitted). If the server runs with strict ANSI settings, the bed_id should be added in the GROUP BY list.Code Snippets
SET @ward_id_to_check = 1 ;
SELECT
st.patient_id,
st.bed_id AS starting_bed_id, -- the first bed a patient uses
-- can be omitted
st.admitted,
MIN(en.discharged) AS discharged
FROM
( SELECT patient_id, bed_id, admitted, discharged
FROM t
WHERE t.ward_id = @ward_id_to_check
AND NOT EXISTS
( SELECT *
FROM t AS prev
WHERE prev.ward_id = @ward_id_to_check
AND prev.patient_id = t.patient_id
AND prev.discharged = t.admitted
)
) AS st
JOIN
( SELECT patient_id, admitted, discharged
FROM t
WHERE t.ward_id = @ward_id_to_check
AND NOT EXISTS
( SELECT *
FROM t AS next
WHERE next.ward_id = @ward_id_to_check
AND next.patient_id = t.patient_id
AND next.admitted = t.discharged
)
) AS en
ON st.patient_id = en.patient_id
AND st.admitted <= en.admitted
GROUP BY
st.patient_id,
st.admitted ;SET @ward_id_to_check = 1 ;
SELECT
st.patient_id,
st.bed_id AS starting_bed_id,
st.admitted,
MIN(en.discharged) AS discharged
FROM
t AS st -- starting period
JOIN
t AS en -- ending period
ON en.ward_id = @ward_id_to_check
AND st.patient_id = en.patient_id
AND NOT EXISTS
( SELECT *
FROM t AS next
WHERE next.ward_id = @ward_id_to_check
AND next.patient_id = en.patient_id
AND next.admitted = en.discharged
)
AND st.admitted <= en.admitted
WHERE
st.ward_id = @ward_id_to_check
AND NOT EXISTS
( SELECT *
FROM t AS prev
WHERE prev.ward_id = @ward_id_to_check
AND prev.patient_id = st.patient_id
AND prev.discharged = st.admitted
)
GROUP BY
st.patient_id,
st.admitted ;Context
StackExchange Database Administrators Q#89454, answer score: 4
Revisions (0)
No revisions yet.