我如何过滤一对多的所有结果

How do i filter all results of a one to many

想象下表:

CREATE TABLE item (code string);
CREATE TABLE item_event (id int, item_code string, event int, date smalldatetime);

item:
code
item1
item2
item3
item4

item_event:
id | item_code  | event | date
1  |  item1     | 123   | 2021-05-04 05:50
2  |  item1     | 456   | 2021-05-04 06:50
3  |  item2     | 123   | 2021-05-04 05:50
4  |  item2     | 678   | 2021-05-04 08:50
5  |  item3     | 456   | 2021-05-04 05:50
6  |  item3     | 890   | 2021-05-04 09:50
7  |  item4     | 123   | 2021-05-04 11:50
8  |  item4     | 456   | 2021-05-04 20:50
9  |  item4     | 890   | 2021-05-04 01:50

如何 select 没有 456 事件的项目或日期在 890 事件之后的 456 事件(如果有的话)?

所以在这种情况下它将 return item2item4.

无法在 SQL 中弄清楚该怎么做。我尝试过的一切都只是过滤掉单个事件,而不是查看“按 item_code”分组的集合。

我们可以尝试使用聚合:

WITH cte AS (
    SELECT item_code
    FROM item_event
    GROUP BY item_code
    HAVING COUNT(CASE WHEN event = 456 THEN 1 END) = 0 OR
           MIN(CASE WHEN event = 890 THEN date END) <
           MIN(CASE WHEN event = 456 THEN date END)
)

SELECT *
FROM item_event
WHERE item_code IN (SELECT item_code FROM cte);

Demo

上述HAVING子句的逻辑是保留任何item_code组具有没有 456事件的记录,任何item_code最早的890事件发生在之前最早的456事件。在这种情况下,这意味着每个 456 事件至少有一个更早的 890 事件。

有两种方法可以做到这一点,不过这取决于您要回答的问题。第一个是 NOT EXISTS,这可能更适合您的第一个场景(没有 456 事件)。看起来像这样:

SELECT Code
FROM dbo.Item I
WHERE NOT EXISTS (SELECT 1
                  FROM dbo.Item_Event IE
                  WHERE IE.Item_Code = I.Code
                    AND IE.Event = 456);

后者对我们来说是 HAVING 条件聚合,这可能更适合您的第二个要求,因为需要比较 2 个不同的事件:

SELECT I.Code
FROM dbo.Item
     JOIN dbo.Item_Event IE ON I.Code = IE.Item_Code --This *might* need to be a LEFT JOIN
GROUP BY I.Code
HAVING COUNT(CASE WHEN Event = 456 THEN 1 END) = 0
    OR MIN(CASE WHEN Event = 890 THEN date END) < MIN(CASE WHEN Event = 456 THEN Date END);

这看起来像是一个典型的 'nested negation' 练习。 当您改写如下时,问题变得更容易在 SQL 中表达:

  • 给我所有没有的物品
    • 伴随的 456 事件
      • 日期早于 456 的 890 事件

每个缩进都成为一个子查询。否定表示为 NOT INNOT EXISTS.

DROP TABLE item IF EXISTS 
DROP TABLE item_event IF EXISTS 
CREATE TABLE item (code string)
CREATE TABLE item_event (id int, item_code string, event int, date smalldatetime)
INSERT INTO item VALUES ('item1'), ('item2'), ('item3'), ('item4')
INSERT INTO item_event VALUES
    (1, 'item1', 123, '2021-05-04 05:50'),
    (2, 'item1', 456, '2021-05-04 06:50'),
    (3, 'item2', 123, '2021-05-04 05:50'),
    (4, 'item2', 678, '2021-05-04 08:50'),
    (5, 'item3', 456, '2021-05-04 05:50'),
    (6, 'item3', 890, '2021-05-04 09:50'),
    (7, 'item4', 123, '2021-05-04 11:50'),
    (8, 'item4', 456, '2021-05-04 20:50'),
    (9, 'item4', 890, '2021-05-04 01:50')

SELECT *
FROM item
WHERE code NOT IN (
    SELECT ie456.item_code
    FROM item_events ie456
    WHERE ie456.event = 456
    AND NOT EXISTS (
        SELECT *
        FROM item_events ie890
        WHERE ie890.item_code = ie456.item_code
        AND ie890.event = 890
        AND ie890.date >= ie456.date
    )
)

输出:

item2
item4