Her bir ardışık satır serisinin toplam süresini bulun


11

MySQL Sürümü

Kod MySQL 5.5'te çalışacaktır

Arka fon

Aşağıdaki gibi bir masam var

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;

Bu tablo hastanedeki hastalar ile ilgilidir ve her hastanın hastaneye yatırılırken biraz zaman geçirdiği yatakları saklar.

Her koğuşta birden fazla yatak olabilir ve her hasta aynı koğuş içinde farklı bir yatağa geçebilir.

Amaç

Yapmak istediğim, her hastanın farklı bir koğuşa taşınmadan belirli bir koğuşta ne kadar zaman geçirdiğini bulmak. Yani aynı koğuşta geçirdiği ardışık sürenin toplam süresini bulmak istiyorum.

Test durumu

-- 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);

Gerçek tabloda satırlar ardışık değildir, ancak her hasta için bir satırdaki boşalma zaman damgası == sonraki satırın kabul zaman damgası.

SQLFiddle

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

Beklenen Sonuç

Aşağıdaki gibi bir şey yazmak istiyorum:

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);

Lütfen, hasta_kimliği ile gruplayamayacağımızı unutmayın. Her yoğun bakım ziyareti için ayrı bir kayıt almalıyız.

Daha açık bir ifadeyle, eğer bir hasta yoğun bakım ünitesinde zaman harcıyor, sonra dışarı çıkar ve oraya geri dönüyorsa, her yoğun bakım ziyaretinde geçirdiği toplam süreyi (yani iki kayıt) almam gerekir.


1
Etkileyici bir soru için +1, karmaşık (ve ilginç) bir sorunu açıkça açıklar. Bir SQLFiddle ek ikramiye için iki kez oy verebilir, ben isterim. Ancak içgüdüm, CTE'ler (ortak tablo ifadeleri) veya pencereleme işlevleri olmadan, bu MySQL'de mümkün olmayacak. Hangi geliştirme ortamını kullanıyorsunuz, yani bunu kod yoluyla yapmak zorunda kalabilirsiniz.
Vérace

@ Vérace YBÜ yataklarına karşılık gelen tüm satırları alan bir kod yazdım ve bunları Python'da gruplandırıyorum.
16:15

Tabii ki bu SQL'de nispeten temiz bir şekilde yapılabilirse bunu tercih edeceğim.
pmav99

Diller ilerledikçe, Python oldukça temiz! :-) MySQL'e bağlı değilseniz ve bir F / LOSS veritabanına ihtiyacınız varsa, CTE ve Pencereleme işlevlerine sahip PostgreSQL'i (birçok yönden MySQL IMHO'dan çok daha üstün) tavsiye edebilir miyim?
Vérace

Yanıtlar:


4

Sorgu 1, SQLFiddle-1'de test edildi

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 ;

Sorgu 2, 1 ile aynıdır ancak türetilmiş tablolar içermez. Bu muhtemelen uygun endekslerle daha iyi bir yürütme planına sahip olacaktır. SQLFiddle-2'de test edin :

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 ;

Her iki sorgu da benzersiz bir kısıtlama olduğunu varsayar (patient_id, admitted). Sunucu katı ANSI ayarlarıyla çalışıyorsa, listeye bed_ideklenmelidir GROUP BY.


Taburcu / kabul tarihleriniz hasta kimlikleri 1 ve 2 için eşleşmediğinden,
kemandaki

2
Huşu içinde - CTE'lerin eksikliği göz önüne alındığında imkansız olduğunu gerçekten düşündüm. Garip bir şekilde, ilk sorgu benim için SQLFiddle - bir aksaklık içinde çalışmaz? İkincisi olsa da, ama yanıltıcı olduğu için st.bed_id kaldırılmasını önerebilir. Hasta 1 ilk kalışının tamamını koğuş 1'de aynı yatakta geçirmedi.
Vérace

@ Vérace, thnx. İlk başta ben de özyinelemeli bir CTE'ye ihtiyacımız olduğunu düşündüm. Hasta_kidindeki eksik bir birleşimi (kimsenin fark etmediğini) düzelttim ve yatakla ilgili noktanızı ekledim.
ypercubeᵀᴹ

@ypercube Cevabınız için çok teşekkür ederim! Bu gerçekten faydalı. Bunu ayrıntılı olarak inceleyeceğim :)
pmav99

0

ÖNERİLEN QUERY

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;

Size örnek verileri dizüstü bilgisayarımdaki yerel bir veritabanına yükledim. Sonra sorguyu çalıştırdım

ÖNERİLEN QUERY EXECUTED

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>

ÖNERİLEN SORGULAMA AÇIKLANDI

AA alt sorgusunda, UNIX_TIMESTAMP () kullanılarak geçen saniye sayısını UNIX_TIMESTAMP(discharged)FROM çıkararak hesaplıyorumUNIX_TIMESTAMP(admitted) . Hasta hala yatakta ise (taburcu olan kişinin belirttiği gibi NULL), şimdiki zamanı ŞİMDİ () atarım . Sonra çıkarmayı yaparım. Bu, hala koğuşta olan herhangi bir hasta için dakika kadar bir süre sağlayacaktır.

Sonra saniye toplamını toplam patient_id. Son olarak, her hasta için saniyeler alıyorum ve hastanın kaldığı saatleri, dakikaları ve saniyeleri görüntülemek için SEC_TO_TIME () kullanıyorum .

BİR ŞANS VER !!!


Kayıt için, bunu Windows 7 dizüstü bilgisayarımda MySQL 5.6.22'de çalıştırdım. SQL Fiddle'da hata veriyor.
RolandoMySQLDBA

1
Cevabınız için çok teşekkür ederim. Korkarım ki bu sorumu cevaplamıyor; muhtemelen açıklamamda yeterince açık değildim. Ne almak istiyorum yoğun bakım her konaklama için harcanan toplam süresidir. Hastaya göre gruplamak istemiyorum. Bir hasta yoğun bakımda zaman harcıyor, sonra dışarı çıkar ve oraya geri dönerse, her ziyarette harcadığı toplam süreyi (yani iki kayıt) almam gerekir.
pmav99

farklı bir konuda, wrt (orijinal) cevabınıza göre iki alt sorgu kullanmanın gerçekten gerekli olmadığını düşünüyorum (yani tablo Ave AA). Bence bunlardan biri yeterli.
pmav99
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.