Ermitteln Sie die Gesamtdauer jeder aufeinanderfolgenden Reihe von Zeilen

11

MySQL-Version

Der Code wird in MySQL 5.5 ausgeführt

Hintergrund

Ich habe eine Tabelle wie die folgende

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;

Diese Tabelle handelt von Patienten in einem Krankenhaus und speichert die Betten, in denen jeder Patient einige Zeit im Krankenhaus verbracht hat.

Jede Station kann mehrere Betten haben und jeder Patient kann in ein anderes Bett innerhalb derselben Station umziehen.

Zielsetzung

Ich möchte herausfinden, wie viel Zeit jeder Patient auf einer bestimmten Station verbracht hat, ohne auf eine andere Station gezogen zu sein. Das heißt, ich möchte die Gesamtdauer der aufeinanderfolgenden Zeit ermitteln, die er auf derselben Station verbracht hat.

Testfall

-- 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:34:00'),
(3, 4, 2, '2015-01-11 12:34:00', NULL),

-- Patient 4 is out of ICU, he gets inserted in ICU without changing any beds
-- and goes back out.
(4, 1, 2, '2015-01-06 06:00:00', '2015-01-07 06:04:00'),
(4, 2, 1, '2015-01-07 06:04:00', '2015-01-07 07:08:00'),
(4, 1, 2, '2015-01-07 07:08:00', '2015-01-08 09:11:00'),

-- Patient 5 is out of ICU, he gets inserted in ICU without changing any beds
-- and he gets dismissed.
(5, 1, 2, '2015-01-06 06:00:00', '2015-01-07 06:04:00'),
(5, 3, 2, '2015-01-07 06:04:00', '2015-01-07 07:08:00'),
(5, 1, 1, '2015-01-07 07:08:00', '2015-01-08 09:11:00'),

-- Patient 6 is inserted in ICU and he is still there
(6, 1, 1, '2015-01-11 12:34:00', NULL);

In der realen Tabelle sind die Zeilen nicht aufeinanderfolgend, sondern für jeden Patienten der Entlassungszeitstempel aus einer Zeile == der Aufnahmezeitstempel der nächsten Zeile.

SQLFiddle

http://sqlfiddle.com/#!2/b5fe5

erwartetes Ergebnis

Ich möchte so etwas wie das Folgende schreiben:

SELECT pid, ward_id, admitted, discharged
FROM  (....)
WHERE ward_id = 1;

(1, 1, '2015-01-06 06:05:00', '2015-01-08 08:11:00'),
(1, 1, '2015-01-08 09:11:00', '2015-01-09 11:11:00'),
(2, 1, '2015-01-07 06:04:00', '2015-01-08 08:11:00'),
(4, 1, '2015-01-07 06:04:00', '2015-01-07 07:08:00'),
(5, 1, '2015-01-07 07:08:00', '2015-01-08 09:11:00'),
(6, 1, '2015-01-11 12:34:00', NULL);

Bitte beachten Sie, dass wir nicht nach patient_id gruppieren können. Wir müssen für jeden Intensivbesuch einen separaten Datensatz abrufen.

Um es klarer auszudrücken: Wenn ein Patient Zeit auf der Intensivstation verbringt, dann aus der Intensivstation auszieht und dann dorthin zurückkehrt, muss ich die Gesamtzeit abrufen, die er bei jedem Besuch auf der Intensivstation verbracht hat (dh zwei Aufzeichnungen).

pmav99
quelle
1
+1 für eine beredte Frage, die ein komplexes (und interessantes) Problem klar erklärt. Wenn ich zweimal für den zusätzlichen Bonus einer SQLFiddle stimmen könnte, würde ich. Mein Instinkt ist jedoch, dass dies ohne CTEs (allgemeine Tabellenausdrücke) oder Fensterfunktionen in MySQL nicht möglich ist. Welche Entwicklungsumgebung verwenden Sie, dh Sie müssen dies möglicherweise über Code tun.
Vérace
@ Vérace Ich habe angegeben, Code zu schreiben, der alle Zeilen abruft, die ICU-Betten entsprechen, und ich gruppiere sie in Python.
pmav99
Wenn dies in SQL relativ sauber möglich ist, werde ich es natürlich vorziehen.
pmav99
Was die Sprachen angeht, ist Python ziemlich sauber! :-) Wenn Sie nicht an MySQL festhalten und eine F / LOSS-Datenbank benötigen, kann ich PostgreSQL empfehlen (das MySQL IMHO in vielerlei Hinsicht weit überlegen ist), das über CTEs und Fensterfunktionen verfügt.
Vérace

Antworten:

4

Abfrage 1, getestet in SQLFiddle-1

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 ;

Abfrage 2, die mit 1 identisch ist, jedoch keine abgeleiteten Tabellen enthält. Dies wird wahrscheinlich einen besseren Ausführungsplan mit geeigneten Indizes haben. 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 ;

Bei beiden Abfragen wird davon ausgegangen, dass eine eindeutige Einschränkung besteht (patient_id, admitted). Wenn der Server mit strengen ANSI-Einstellungen ausgeführt wird, bed_idsollte dies in die GROUP BYListe aufgenommen werden.

ypercubeᵀᴹ
quelle
Beachten Sie, dass ich die Einfügewerte in der Geige geändert habe, da Ihre entlassenen / zugelassenen Daten nicht mit den Patienten-IDs 1 und 2
übereinstimmten
2
Ehrfurcht - ich dachte wirklich, dass es angesichts des Mangels an CTEs unmöglich ist. Seltsamerweise würde die erste Abfrage in SQLFiddle nicht für mich ausgeführt - eine Panne? Der zweite hat es zwar getan, aber darf ich vorschlagen, dass die st.bed_id entfernt wird, da dies irreführend ist. Patient 1 verbrachte nicht seinen gesamten ersten Aufenthalt auf Station 1 im selben Bett.
Vérace
@ Vérace, danke. Zuerst dachte ich auch, dass wir einen rekursiven CTE brauchen. Ich habe eine fehlende Verknüpfung auf patient_id korrigiert (die niemand bemerkt hat;) und Ihren Standpunkt zum Bett hinzugefügt.
Ypercubeᵀᴹ
@ypercube Vielen Dank für Ihre Antwort! Das ist wirklich hilfreich. Ich werde dies im Detail studieren :)
pmav99
0

Vorgeschlagene Abfrage

SELECT patient_id,SEC_TO_TIME(SUM(elapsed_time)) elapsed
FROM (SELECT * FROM (SELECT patient_id,
UNIX_TIMESTAMP(IFNULL(discharged,NOW())) -
UNIX_TIMESTAMP(admitted) elapsed_time
FROM t WHERE ward_id = 1) AA) A
GROUP BY patient_id;

Ich habe Ihre Beispieldaten in eine lokale Datenbank auf meinem Laptop geladen. Dann habe ich die Abfrage ausgeführt

Vorgeschlagene Abfrage ausgeführt

mysql> SELECT patient_id,SEC_TO_TIME(SUM(elapsed_time)) elapsed
    -> FROM (SELECT * FROM (SELECT patient_id,
    -> UNIX_TIMESTAMP(IFNULL(discharged,NOW())) -
    -> UNIX_TIMESTAMP(admitted) elapsed_time
    -> FROM t WHERE ward_id = 1) AA) A
    -> GROUP BY patient_id;
+------------+-----------+
| patient_id | elapsed   |
+------------+-----------+
|          1 | 76:06:00  |
|          2 | 26:07:00  |
|          4 | 01:04:00  |
|          5 | 26:03:00  |
|          6 | 118:55:48 |
+------------+-----------+
5 rows in set (0.00 sec)

mysql>

Vorgeschlagene Anfrage erklärt

In der Unterabfrage AA berechne ich die Anzahl der Sekunden, die mit UNIX_TIMESTAMP () vergangen sind, indem ichUNIX_TIMESTAMP(discharged) FROM subtrahiere UNIX_TIMESTAMP(admitted). Befindet sich der Patient noch im Bett (wie durch entlassenes Wesen angezeigt NULL), weise ich JETZT die aktuelle Zeit zu () . Dann subtrahiere ich. Auf diese Weise erhalten Sie für jeden Patienten, der sich noch auf der Station befindet, eine minutengenaue Dauer.

Dann summiere ich die Summe der Sekunden nach patient_id. Schließlich nehme ich die Sekunden für jeden Patienten und verwende SEC_TO_TIME () , um Stunden, Minuten und Sekunden des Patientenaufenthalts anzuzeigen.

VERSUCHE ES !!!

RolandoMySQLDBA
quelle
Für die Aufzeichnung habe ich dies in MySQL 5.6.22 auf meinem Windows 7-Laptop ausgeführt. Es gibt einen Fehler in SQL Fiddle.
RolandoMySQLDBA
1
ich danke Ihnen sehr für Ihre Antwort. Ich befürchte jedoch, dass dies meine Frage nicht beantwortet; wahrscheinlich war ich in meiner Beschreibung nicht klar genug. Was ich abrufen möchte, ist die Gesamtzeit, die für jeden Aufenthalt auf der Intensivstation aufgewendet wird. Ich möchte nicht nach Patienten gruppieren. Wenn ein Patient Zeit auf der Intensivstation verbringt, dann die Intensivstation verlässt und dann dorthin zurückkehrt, muss ich die Gesamtzeit abrufen, die er bei jedem Besuch verbracht hat (dh zwei Aufzeichnungen).
pmav99
Zu einem anderen Thema, schreiben Sie auf Ihre (ursprüngliche) Antwort. Ich denke, dass die Verwendung von zwei Unterabfragen nicht wirklich notwendig ist (dh Tabelle Aund AA). Ich denke, dass einer von ihnen genug ist.
pmav99