Вопрос по sql – Разница в дате между последовательными строками - сложная

8

Я ранее разместилвопрос на который был дан ответ, но мне тоже нужен запрос для этого. У меня есть структура таблицы с такими данными (даты в форматеdd/mm/yyyy).

<code>ID    Account Number    Unit    Admit_Date    Disch_Date
1     1001              w32     01/04/2012    
2     1002              w32     01/04/2012    01/04/2012
3     1001              ccu     03/04/2012
4     1001              w33     05/04/2012
5     1003              cicu    04/04/2012
6     1001              ccu     07/04/2012
7     1001              ccu     07/04/2012    10/04/2012
8     1003              w33     05/04/2012
9     1003              w33     05/04/2012    08/04/2012
</code>

В основном эта таблица касается пациентов, поступающих в определенную палату и переводимых между палатами, а затем, наконец, выписанных либо в тот же день, либо через несколько дней. Ожидаемый результат от запроса будет:

<code>Account_Number                                 No. Of Days
1001              01/04/2012    03/04/2012      2
1001              03/04/2012    05/04/2012      2
1001              05/03/2012    07/04/2012      2
1001              07/04/2012    10/04/2012      3
1002              01/04/2012    01/04/2012      0
1003              04/04/2012    05/04/2012      1
1003              05/04/2012    08/04/2012      3
</code>

Поле даты выписки будет заполнено только при выписке пациента, поэтому я хотел бы рассчитать разницу в датах между каждой датой перемещения пациента, включая как госпитализацию, так и дату выписки.

Я использую MS Access 2003.

Я надеюсь, что кто-нибудь сможет помочь мне с этим.

+1 за вопрос с приличными образцами данных и необходимыми результатами. Было бы на один шаг лучше, если бы вы дали имя таблицы (удивительно, как часто вопросы опускаются, что означает, что те, кто отвечает на вопрос, должны придумать имя таблицы). Jonathan Leffler
Для заданных пар строк, таких как ID = {6,7} и ID = {8,9}, правильно ли говорить, что строка, в которой находится пациент (номер счета), единица измерения и дата приема с нулевой датой выписки, игнорируется также запись для того же пациента, единицы и даты госпитализации, но ненулевая дата выписки? Jonathan Leffler
Да, это было бы правильно, так как у нас есть интерфейс, который собирает данные, и именно так они будут заполняться !!! Mohammed Rishal

Ваш Ответ

1   ответ
5

Filtering out the irrelevant data

С любым сложным запросом частью искусства является построение запроса по частям, тестирование на ходу.

Я предполагаю, что имя таблицы - PatientMovements и что:

Given pairs of rows like ID = {6,7} and ID = {8,9}, it is correct to say that the row where the patient (account number), unit and admission date with null discharge date is ignored when there is also a record for the same patient, unit and admit date but a non-null discharge date.

Итак, первый шаг - создать строки, над которыми нам нужно поработать, отфильтровывая нерелевантные данные из таблицы, записанной в базе данных. Это СОЮЗ из двух наборов данных:

  1. Those rows with a non-null discharge date.
  2. Those rows with a null discharge date but no row for the same account, unit and admission date.

Понятно, что первая часть UNION это:

SELECT * FROM PatientMovements WHERE DischargeDate IS NOT NULL

Менее очевидно, вторая часть СОЮЗА:

SELECT *
  FROM PatientMovements AS p1
 WHERE DischargeDate IS NULL
   AND NOT EXISTS
       (SELECT *
          FROM PatientMovements AS P2
         WHERE P1.Account   = P2.Account
           AND P1.Unit      = P2.Unit
           AND P1.AdmitDate = P2.AdmitDate
           AND P2.DischargeDate IS NOT NULL
       )

Теперь вы можете объединить их в один набор результатов:

SELECT *
  FROM PatientMovements
 WHERE DischargeDate IS NOT NULL
UNION
SELECT *
  FROM PatientMovements AS p1
 WHERE DischargeDate IS NULL
   AND NOT EXISTS
       (SELECT *
          FROM PatientMovements AS P2
         WHERE P1.Account   = P2.Account
           AND P1.Unit      = P2.Unit
           AND P1.AdmitDate = P2.AdmitDate
           AND P2.DischargeDate IS NOT NULL
       )

Вы можете проверить запрос выше, проверив, что он возвращает строки с идентификаторами 1..5, 7 и 9.

Warning: непроверенный код. Ни один из SQL в этом ответе не был рядом с СУБД, поэтому он не проверен.

Applying Lessons Learned Previously

И тогда вы можете применить свое обучение от другихвопрос упорядочить данные и рассчитать разницу в датах и т. д. Единственная сложность заключается в том, что вам нужно дважды выписать этот запрос, что является болезненным (если только MS Access 2003 не поддерживает предложение «WITH» или общее табличное выражение).


But would there be no single query to obtain this required output?

Конечно, UNION - это отдельный запрос. Я полагаю, вы могли бы просто написать:

SELECT *
  FROM PatientMovements
 WHERE (DischargeDate IS NOT NULL)
    OR (DischargeDate IS     NULL
        AND NOT EXISTS
            (SELECT *
               FROM PatientMovements AS P2
              WHERE P1.Account   = P2.Account
                AND P1.Unit      = P2.Unit
                AND P1.AdmitDate = P2.AdmitDate
                AND P2.DischargeDate IS NOT NULL
            )
       )

Я не могу сразу думать о более компактном способе выполнения запроса.


Building the UNION into 'The Other Answer'

Принятый ответ на другой вопрос имеет два возможных решения (с изменениями в комментариях и переформатировании):

SELECT T1.ID, T1.AccountNumber, T1.Date, 
       MIN(T2.Date) AS NextDate, 
       DATEDIFF("D", T1.Date, MIN(T2.Date)) AS DaysDiff
  FROM YourTable T1
  JOIN YourTable T2
    ON T1.AccountNumber = T2.AccountNumber AND T2.Date > T1.Date

Или же:

SELECT ID, AccountNumber, Date, NextDate,
       DATEDIFF("D", Date, NextDate) AS DaysDiff
  FROM (SELECT ID, AccountNumber, Date,
               (SELECT MIN(Date) 
                  FROM YourTable T2
                 WHERE T2.AccountNumber = T1.AccountNumber
                   AND T2.Date > T1.Date
               ) AS NextDate
          FROM YourTable T1
        ) AS T

Как отмечено в комментарии, отсутствие имени таблицы в вопросе приводит к тому, что в ответе появляются разные имена таблиц; то, что я назвал PatientMovements, в этом ответе называлось YourTable. Другое отличие состоит в том, что исходный вопрос не включал в данные столбцы Unit или DischargeDate. Однако запрос UNION, который я дал, дает соответствующие данные для выполнения этих запросов, поэтому все, что остается сделать, - это записать запрос UNION в другие ответы вместо YourTable. Это ведет к:

SELECT T1.ID, T1.AccountNumber, T1.Date, 
       MIN(T2.Date) AS NextDate, 
       DATEDIFF("D", T1.Date, MIN(T2.Date)) AS DaysDiff
  FROM (SELECT *
          FROM PatientMovements
         WHERE (DischargeDate IS NOT NULL)
            OR (DischargeDate IS     NULL
                AND NOT EXISTS
                    (SELECT *
                       FROM PatientMovements AS P2
                      WHERE P1.Account   = P2.Account
                        AND P1.Unit      = P2.Unit
                        AND P1.AdmitDate = P2.AdmitDate
                        AND P2.DischargeDate IS NOT NULL
                    )
               )
       ) AS T1
  JOIN (SELECT *
          FROM PatientMovements
         WHERE (DischargeDate IS NOT NULL)
            OR (DischargeDate IS     NULL
                AND NOT EXISTS
                    (SELECT *
                       FROM PatientMovements AS P2
                      WHERE P1.Account   = P2.Account
                        AND P1.Unit      = P2.Unit
                        AND P1.AdmitDate = P2.AdmitDate
                        AND P2.DischargeDate IS NOT NULL
                    )
               )
       ) AS T2
    ON T1.AccountNumber = T2.Accountnumber AND T2.Date > T1.Date

Или же:

SELECT ID, AccountNumber, Date, NextDate,
       DATEDIFF("D", Date, NextDate) AS DaysDiff
  FROM (SELECT ID, AccountNumber, Date,
               (SELECT MIN(Date) 
                  FROM (SELECT *
                          FROM PatientMovements
                         WHERE (DischargeDate IS NOT NULL)
                            OR (DischargeDate IS     NULL
                                AND NOT EXISTS
                                    (SELECT *
                                       FROM PatientMovements AS P2
                                      WHERE P1.Account   = P2.Account
                                        AND P1.Unit      = P2.Unit
                                        AND P1.AdmitDate = P2.AdmitDate
                                        AND P2.DischargeDate IS NOT NULL
                                    )
                               )
                       ) AS T2
                 WHERE T2.Accountnumber = T1.AccountNumber
                   AND T2.Date > T1.Date
               ) AS NextDate
          FROM (SELECT *
                  FROM PatientMovements
                 WHERE (DischargeDate IS NOT NULL)
                    OR (DischargeDate IS     NULL
                        AND NOT EXISTS
                            (SELECT *
                               FROM PatientMovements AS P2
                              WHERE P1.Account   = P2.Account
                                AND P1.Unit      = P2.Unit
                                AND P1.AdmitDate = P2.AdmitDate
                                AND P2.DischargeDate IS NOT NULL
                            )
                       )
               ) AS T1
        ) AS T

Таким образом, если вы осторожны и разрабатываете фрагменты запросов, а затем последовательно их комбинируете, можно приручить наиболее ужасно выглядящий запрос.

Common Table Expressions

Обратите внимание, что стандарт SQL имеет «общие табличные выражения»; (CTEs) aka «WITH оговорки» что может сделать вещи еще проще.

WITH YourTable AS
   (SELECT *
      FROM PatientMovements
     WHERE (DischargeDate IS NOT NULL)
        OR (DischargeDate IS     NULL
            AND NOT EXISTS
                (SELECT *
                   FROM PatientMovements AS P2
                  WHERE P1.Account   = P2.Account
                    AND P1.Unit      = P2.Unit
                    AND P1.AdmitDate = P2.AdmitDate
                    AND P2.DischargeDate IS NOT NULL
                )
           )
     )
SELECT T1.ID, T1.AccountNumber, T1.Date, 
       MIN(T2.Date) AS NextDate, 
       DATEDIFF("D", T1.Date, MIN(T2.Date)) AS DaysDiff
  FROM YourTable T1
  JOIN YourTable T2
    ON T1.AccountNumber = T2.AccountNumber AND T2.Date > T1.Date

Или же:

WITH YourTable AS
   (SELECT *
      FROM PatientMovements
     WHERE (DischargeDate IS NOT NULL)
        OR (DischargeDate IS     NULL
            AND NOT EXISTS
                (SELECT *
                   FROM PatientMovements AS P2
                  WHERE P1.Account   = P2.Account
                    AND P1.Unit      = P2.Unit
                    AND P1.AdmitDate = P2.AdmitDate
                    AND P2.DischargeDate IS NOT NULL
                )
           )
     )
SELECT ID, AccountNumber, Date, NextDate,
       DATEDIFF("D", Date, NextDate) AS DaysDiff
  FROM (SELECT ID, AccountNumber, Date,
               (SELECT MIN(Date) 
                  FROM YourTable T2
                 WHERE T2.AccountNumber = T1.AccountNumber
                   AND T2.Date > T1.Date
               ) AS NextDate
          FROM YourTable T1
        ) AS T

Одним из основных преимуществ использования CTE является то, что оптимизатору явно сказано, что выражения таблиц одинаковы во всех местах, где он используется, тогда как при его записи несколько раз он может не обнаружить эту общность. Кроме того, написание запроса несколько раз открывает возможность того, что два «означают одинаковые». запросы на самом деле немного отличаются из-за ошибки редактирования; эта возможность исключена CTE. Другое преимущество в текущем контексте заключалось в том, что объединение CTE с решениями другого вопроса было детской игрой.

К сожалению для вас, вряд ли MS Access 2003 поддерживает CTE. Я разделяю твою боль; СУБД, с которой я работаю, также в основном не работает.

Спасибо, ребята, но не было бы ни одного запроса для получения этого требуемого вывода? Как это слишком сложно для меня! @GarethD Mohammed Rishal
Вам сказали, как сделать различия из одной таблицы в вашем другом вопросе. Чтобы сделать все это в одном запросе, вы пишете один из указанных выше запросов каждый раз, когда таблица упоминается в ответе на ваш другой вопрос. Это не ракетостроение; это просто немного неудобно. Но вам нужно научиться справляться с этим.
Access 2003 не поддерживает общие табличные выражения, однако вы можете ссылаться на сохраненные запросы. Таким образом, вы можете сохранить вышеупомянутый запрос, а затем подставить имя запроса в запрос из вашего другого вопроса.
Привет, спасибо за помощь. Я надеялся, что смогу получить требуемое o / p (разность дат между номерами счетов) и вышеуказанный запрос в одном выражении вместо того, чтобы выполнять 2 запроса по отдельности. Mohammed Rishal

Похожие вопросы