复杂的SQL查询(至少对我而言)

时间:2009-02-02 16:18:08

标签: sql

我正在尝试开发一个返回序列号列表的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。

这可以在带有子查询的签名查询中完成吗?

11 个答案:

答案 0 :(得分:4)

select * from Table1 
group by Step 
having count(*) % 2 = 1

这是当没有两个'enter'但每个enter后跟一个'exit'(如提供的例子中所示)

答案 1 :(得分:4)

我个人认为这是通过改变数据存储方式最好的方法。目前的方法不能有效或有效。是的,你可以乱七八糟地找到一种方法来获取数据。但是,当您有多个输入的步骤而没有退出同一个serialNO时会发生什么?是的,它不应该发生,但迟早它会发生,除非你编写代码来防止它(代码哪个联合很难写)。拥有一个将enter和exit存储在同一记录中的表会更清晰。然后,查询(并且更快)查找输入但未退出的内容变得微不足道。

答案 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

答案 3 :(得分:2)

我在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的重要性在这里,但上述内容很容易修改,以纳入日内或跨日要求。

答案 4 :(得分:1)

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

答案 5 :(得分:1)

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
)

答案 6 :(得分:0)

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

答案 7 :(得分:0)

如果您使用的是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

答案 8 :(得分:0)

在Oracle中:

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 过滤exits没有enters

的情况

答案 9 :(得分:0)

有些人建议重新安排您的数据,但我没有看到任何示例,所以我会对此进行修改。这是您描述的同一个表的部分非规范化变体。它应该适用于有限数量的“步骤”(这个例子只考虑“输入”和“退出”,但它可以很容易地扩展),但它最大的弱点是在填充表格后添加额外的步骤(比如说,输入/处理/退出)很昂贵 - 您必须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

答案 10 :(得分:0)

这是一个适用于您的方案的简单查询

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

我已经测试了这个,这将为您提供序列号为1&amp;的行。 2