我有以下表结构:
物品
ID | Name
--------
1 | Apple
2 | Pear
3 | Banana
4 | Plum
5 | Tomato
事件
ItemStart | ItemEnd | EventType | EventDate
--------------------------------------------
1 | 2 | Planted | 2014-01-01
1 | 3 | Picked | 2014-01-02
3 | 5 | Eaten | 2014-01-05
这两个表仅由Item的主键和Event中的ItemStart和ItemEnd(包括)的范围链接。事件始终引用连续的项目序列,但并非所有给定项目的事件都具有相同的范围。事件永远不会发生在给定项目的同一日期。
我想要制作的查询如下:
List all the Items, and for each Item show the most recent Event
示例输出:
ID | Name | Event | Date
----------------------------
1 | Apple | Picked | 2014-01-02 (Planted then Picked)
2 | Pear | Picked | 2014-01-02 (Planted then Picked)
3 | Banana | Eaten | 2014-01-05 (Picked then Eaten)
4 | Plum | Eaten | 2014-01-05 (Eaten)
5 | Tomato | Eaten | 2014-01-05 (Eaten)
从表面上看这似乎是合情合理的,如果有传统的外键关系(想象ItemID
而不是ItemStart
和ItemEnd
)我就会这样做可能会加入一个相关的子查询,如下所示:
SELECT Name, EventType, EventDate
FROM Item i
INNER JOIN (
SELECT ItemID, EventType, EventDate
FROM Event e
WHERE EventDate = (SELECT MAX(EventDate) FROM Event e_max WHERE e_max.ItemID = e.ItemID)
) latest_events ON i.ID = latest_events.ItemID
然而,由于范围关系已经到位,我想做更多类似的事情,但它不起作用:
SELECT Name, EventType, EventDate
FROM Item i
INNER JOIN (
SELECT ItemStart, ItemEnd, EventType, EventDate
FROM Event e
WHERE EventDate = (SELECT MAX(EventDate) FROM Event e_max WHERE i.ID >= e_max.ItemStart AND i.ID <= e_max.ItemEnd)
) latest_events ON i.ID >= latest_events.ItemStart AND i.ID <= latest_events.ItemEnd
我在第6行收到关于i.ID >= e_max.ItemStart AND i.ID <= e_max.ItemEnd
的错误,因为您无法在联接的其他部分引用i
。我想这样做(在更简单的例子中并不是必需的)因为当我构建子查询时我不再需要一个ID来链接 - 重叠范围意味着有很多可能的方法包括单个项目,所以我想直接引用该项目,其ID仅在顶级项目表中可用。
我希望这是有道理的。
我正在使用SQL Server 2008 R2。这是一个将在一夜之间运行的报告,因此速度并不是那么重要,但是有很多项目(百万分之一);虽然针对每个项目有多个事件,但使用大范围意味着事件记录要少得多。
我曾经想过的事情:
如何生成此查询?提前谢谢!
答案 0 :(得分:3)
您可以使用CTE
和row_number()
。
<强> SQL Fiddle Demo 强>
;with cte as
(
SELECT *,
ROW_NUMBER() OVER (PARTITION BY i.id ORDER BY e.EventDate DESC) as rNum
FROM Item i
JOIN Event e
ON i.id between e.ItemStart and e.ItemEnd
)
SELECT ID,
Name,
EventType,
EventDate FROM cte
WHERE rNum = 1
基本上,CTE已加入项目和事件,并为rownumber添加了一个新列,并在item.ID上进行了分区。这是它的外观截图。从这里我只选择rNum = 1,它应该是每个item.id的最大事件日期。
答案 1 :(得分:1)
这应该类似于其他最佳组合和加入范围的解决方案:
SELECT * FROM
Item i INNER JOIN
Event e ON i.id BETWEEN e.ItemStart AND e.ItemEnd
WHERE NOT EXISTS ( -- exclude non-last events
SELECT * FROM Event
WHERE
i.id between ItemStart and ItemEnd
AND e.EventDate < EventDate)