我如何过滤一对多的所有结果
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 item2
和 item4
.
无法在 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);
上述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 IN
或 NOT 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
想象下表:
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 item2
和 item4
.
无法在 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);
上述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 事件
- 不伴随的 456 事件
每个缩进都成为一个子查询。否定表示为 NOT IN
或 NOT 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