连续行之间的日期差异 - 复杂

时间:2012-04-11 01:06:05

标签: sql ms-access

我之前发布的question已经回复,但我也需要查询。 我有一个包含这样数据的表结构(日期格式为 dd / mm / yyyy )。

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

基本上,这个表格涉及患者入住特定病房并在病房之间转移,然后在同一天或几天后最终出院。 查询的预期结果是:

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

出院日期字段仅在患者出院时填写,因此我想计算患者每个移动日期之间的日期差异,包括入院时间和出院日期。

我使用MS Access 2003。

我希望有人能够帮助我。

1 个答案:

答案 0 :(得分:5)

过滤掉无关数据

对于任何复杂的查询,本领域的一部分是逐个构建查询,随时进行测试。

我假设表名是PatientMovements,并且:

  

给定ID = {6,7}和ID = {8,9}等行, 正确地说明患者所在的行(帐号),单位和入场如果同一患者,单位和入院日期的记录,但非空出院日期,则忽略带有出院日期的日期。

因此,第一步是生成我们需要处理的行,从数据库中记录的表中过滤掉不相关的数据。这是两组数据的联合:

  1. 具有非空出院日期的那些行。
  2. 那些排放日期为空但行没有同一帐户,单位和入场日期的行。
  3. 显然,UNION的第一部分是:

    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
           )
    

    现在您可以将它们组合成一个结果集:

    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
           )
    

    您可以通过检查它是否返回ID为1..5,7和9的行来验证上述查询。

    警告:未经测试的代码。这个答案中没有一个SQL靠近DBMS,所以它没有经过测试。

    以前学过的经验教训

    然后你可以从其他question应用你的学习来排序数据并计算日期差异等。唯一的复杂因素是你必须将该查询写出两次,这很痛苦(除非MS Access 2003支持'WITH'子句或公用表表达式。)


      

    但是没有单一的查询来获得这个必需的输出吗?

    当然,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
                )
           )
    

    我无法立即想到更简洁的查询方式。


    将UNION构建为“其他答案”

    另一个问题的接受答案有两种可能的解决方案(经评论修改并重新格式化):

    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
    

    所以,只要你小心,并在片段中开发查询,然后一致地组合它们,就可以驯服最糟糕的查询。

    公用表格表达式

    请注意,SQL标准具有“公用表表达式”(CTE),即“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。我分享你的痛苦;我工作的DBMS主要也没有。

相关问题