【问题标题】:How do i filter all results of a one to many我如何过滤一对多的所有结果
【发布时间】:2021-09-27 08:00:13
【问题描述】:

想象以下表格:

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

我如何选择没有456事件或日期在890事件之后的456事件的项目?

所以在这种情况下,它将返回 item2item4

无法弄清楚如何在 SQL 中执行此操作。我尝试过的所有事情都只是过滤掉单个事件,而不是查看“按 item_code 分组”的集合。

【问题讨论】:

  • 查看NOT EXISTS
  • 您想要item 还是所有item_event

标签: sql sql-server one-to-many


【解决方案1】:

我们可以尝试使用聚合:

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 子句的逻辑是保留任何具有没有 456 事件的item_code 记录集, 任何item_code最早的 890 事件发生在最早的 456 事件之前。在这种情况下,这意味着每个 456 事件至少有一个更早的 890 事件。

【讨论】:

  • 好的...但是您接受的解决方案的特点是第一个查询不正确,第二个查询与我的答案相同。
【解决方案2】:

有两种方法可以做到这一点,但这取决于您要回答的问题。第一个是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,由于需要比较两个不同的事件,这可能更适合您的第二个要求:

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

【讨论】:

    【解决方案3】:

    这看起来像是一个典型的“嵌套否定”练习。 当您将问题改写如下时,该问题在 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
    

    【讨论】:

      猜你喜欢
      • 2014-08-13
      • 2020-02-12
      • 1970-01-01
      • 2013-10-06
      • 2019-10-14
      • 2014-10-17
      • 2010-11-10
      • 2011-04-01
      • 1970-01-01
      相关资源
      最近更新 更多