【问题标题】:Complex SQL Query (at least for me)复杂的 SQL 查询(至少对我来说)
【发布时间】:2010-03-06 04:04:35
【问题描述】:

我正在尝试开发一个将返回序列号列表的 sql 查询。该表设置为每当序列号到达一个步骤时,输入日期和时间。当它完成该步骤时,将输入另一个日期和时间。我想开发一个查询,它将为我提供已进入步骤但未退出步骤的序列号列表。他们可能多次进入,所以我只寻找没有出口的序列号并进入。

例如(为方便使用,将表格命名为“Table1”)

 1. Serial | Step  | Date

 2. 1      | enter | 10/1
 3. 1      | exit  | 10/2
 4. 1      | enter | 10/4
 5. 2      | enter | 10/4
 6. 3      | enter | 10/5
 7. 3      | exit  | 10/6

对于上表,序列号 1 和 2 应该被检索,但 3 不应该。

这可以在带有子查询的单一查询中完成吗?

【问题讨论】:

    标签: sql


    【解决方案1】:
    select * from Table1 
    group by Step 
    having count(*) % 2 = 1
    

    这是指不能有两个“输入”但每个输入后跟一个“退出”(如提供的示例中所示)

    【讨论】:

    • 他特别指出可能有多个输入
    • 但从未说过多次进入没有退出。
    • 您实际上不能使用 GROUP BY 选择 *。原始海报只是将其作为 SELECT [Step] FROM Table1 GROUP BY [Step] HAVING COUNT(*) % 2 = 1 的简写。另外值得注意的是,模运算符 (%) 不是 ANSI,但可以广泛使用。
    • “拥有 count(*) % 2 = 1”的诀窍是什么,谁能解释一下?
    【解决方案2】:

    我个人认为最好通过改变数据存储方式来完成。当前的方法不可能是有效的或有效的。是的,您可以四处寻找方法来获取数据。但是,当您输入了多个步骤而同一个序列号没有退出时会发生什么?是的,它不应该发生,但迟早会发生,除非您编写了代码来防止它(代码可能会变得复杂编写)。有一个表将进入和退出存储在同一记录中会更干净。然后查询(并且更快)以找到那些进入但未退出的人变得微不足道。

    【讨论】:

    • 我的第一个想法是,最好有一个带有进入日期和退出日期(允许为空)字段的记录。如果是“每个序列号可以多次进出”的情况,那么你可以有一个表将多个进出记录映射到一个序列号。
    【解决方案3】:

    这将为您提供所有没有结尾“退出”的“进入”记录。如果您只需要序列号列表,则还应按序列号分组并仅选择该列。

    SELECT t1.* 
    FROM Table1 t1
    LEFT JOIN Table1 t2 ON t2.Serial=t1.Serial 
        AND t2.Step='Exit' AND t2.[Date] >= t1.[Date]
    WHERE t1.Step='Enter' AND t2.Serial IS NULL
    

    【讨论】:

      【解决方案4】:

      我在 MySQL 中对此进行了测试。

      SELECT Serial, 
        COUNT(NULLIF(Step,'enter')) AS exits, 
        COUNT(NULLIF(Step,'exit')) AS enters 
      FROM Table1
        WHERE Step IN ('enter','exit')
      GROUP BY Serial
      HAVING enters <> exits
      

      我不确定 Date 在这里的重要性,但可以轻松修改以上内容以纳入当日或跨日要求。

      【讨论】:

        【解决方案5】:
        SELECT DISTINCT Serial
        FROM Table t
        WHERE (SELECT COUNT(*) FROM Table t2 WHERE t.Serial = t2.Serial AND Step = 'exit') <
              (SELECT COUNT(*) FROM Table t2 WHERE t.Serial = t2.Serial AND Step = 'enter')
        

        【讨论】:

          【解决方案6】:
          SELECT * FROM Table1 T1
          WHERE NOT EXISTS (
            SELECT * FROM Table1 T2   
            WHERE T2.Serial = T1.Serial 
              AND T2.Step = 'exit'
              AND T2.Date > T1.Date
          )
          

          【讨论】:

            【解决方案7】:

            如果您确定您不想要的输入值和退出值匹配,则可以查找“输入”计数不等于“输入”计数的所有序列值“退出”。

            【讨论】:

              【解决方案8】:

              如果您使用的是 MS SQL 2005 或 2008,您可以使用 CTE 来获得您正在寻找的结果...

              WITH ExitCTE
              AS
                  (SELECT Serial, StepDate
                  FROM    #Table1
                  WHERE   Step = 'exit')
              SELECT  A.*
              FROM    #Table1 A LEFT JOIN ExitCTE B ON A.Serial = B.Serial AND B.StepDate > A.StepDate
              WHERE   A.Step = 'enter'
                      AND B.Serial IS NULL
              

              如果您不使用这些,我会尝试使用子查询...

              SELECT  A.*
              FROM    #Table1 A LEFT JOIN (SELECT Serial, StepDate
                                          FROM    #Table1
                                          WHERE   Step = 'exit') B 
                          ON A.Serial = B.Serial AND B.StepDate > A.StepDate
              WHERE   A.Step = 'enter'
                      AND B.Serial IS NULL
              

              【讨论】:

                【解决方案9】:

                在甲骨文中:

                SELECT *
                FROM (
                 SELECT serial,
                 CASE
                   WHEN so < 0 THEN "Stack overflow"
                   WHEN depth > 0 THEN "In"
                   ELSE "Out"
                 END AS stack
                 FROM (
                  SELECT serial, MIN(SUM(DECODE(step, "enter", 1, "exit", -1) OVER (PARTITION BY serial ORDER BY date)) AS so, SUM(DECODE(step, "enter", 1, "exit", -1)) AS depth
                  FROM Table 1
                  GROUP BY serial
                 )
                )
                WHERE stack = "Out"
                

                这将选择你想要的AND过滤掉没有enters而发生的exits

                【讨论】:

                  【解决方案10】:

                  有几个人建议重新排列您的数据,但我没有看到任何示例,所以我会尝试一下。这是您描述的同一表的部分非规范化变体。它应该适用于有限数量的“步骤”(这个例子只考虑了“进入”和“退出”,但它可以很容易地扩展),但它最大的缺点是在填充表格后添加额外的步骤(比如, enter/process/exit) 很昂贵——你必须ALTER TABLE 这样做。

                  serial  enter_date  exit_date
                  ------  ----------  ---------
                       1        10/1       10/2
                       1        10/4       NULL
                       2        10/4       NULL
                       3        10/5       10/6
                  

                  你的查询就变得很简单了:

                  SELECT serial,enter_date FROM table1 WHERE exit_date IS NULL;
                  
                  serial  enter_date
                  ------  ----------
                       1        10/4
                       2        10/4
                  

                  【讨论】:

                    【解决方案11】:

                    这是一个适用于您的场景的简单查询

                    SELECT Serial FROM Table1 t1
                    WHERE Step='enter' 
                    AND (SELECT Max(Date) FROM Table1 t2 WHERE t2.Serial = t1.Serial) = t1.Date
                    

                    我已经对此进行了测试,这将为您提供序列号为 1 和 2 的行

                    【讨论】:

                      猜你喜欢
                      • 1970-01-01
                      • 2013-06-21
                      • 2022-01-16
                      • 2011-12-16
                      • 1970-01-01
                      • 1970-01-01
                      • 2010-11-03
                      • 1970-01-01
                      • 2018-02-08
                      相关资源
                      最近更新 更多