【问题标题】:SQL join to correlated subquery where tables are related by overlapping rangesSQL 连接到相关子查询,其中表通过重叠范围相关
【发布时间】:2014-09-10 20:38:18
【问题描述】:

我的表结构如下:

物品

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 而不是ItemStartItemEnd),我可能会加入相关子查询像这样:

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 &gt;= e_max.ItemStart AND i.ID &lt;= e_max.ItemEnd 的错误,因为您无法从连接的另一部分引用 i。我想这样做(这在更简单的示例中不是必需的),因为当我构建子查询时,我不再有一个 ID 可以链接到 - 重叠范围意味着有很多可能的方式来包含单个 Item ,因此我想直接引用该项目,其 ID 仅在顶级项目表中可用。

我希望这是有道理的。

我使用的是 SQL Server 2008 R2。这是针对将在一夜之间运行的报告,因此速度并不重要,但有很多项目(百万);虽然每个项目都有多个事件,但使用大范围意味着事件记录要少得多。

我想到的事情:

  • 以某种方式扩展项目/事件关系,以便针对所有单个项目记录事件。这将导致所考虑的数据量显着增加,但允许使用更简单的查询方法。
  • 以某种方式处理事件以约束或合并范围 - 如果我知道对于给定项目,它的所有事件都具有相同的开始和结束,我也许可以简化事情。没有完全考虑清楚。

如何生成此查询?提前致谢!

【问题讨论】:

  • 如果我理解的话,我不知道,因为我无法遵循您预期的示例输出。也许您可以使用CREATE VIEW V_EVENT as Select ItemStart as Item,EventType,Eventdate UNION Select ItemEnd,EventType,Eventdate 的视图更传统地加入它

标签: sql sql-server sql-server-2008 greatest-n-per-group correlated-subquery


【解决方案1】:

您可以使用CTErow_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 已经加入了 item 和 event,并为 rownumber 添加了一个新列,并在 item.ID 上进行了分区。这是它的外观截图。从这里我只选择 rNum = 1 这应该是每个 item.id 的最大事件日期。

【讨论】:

    【解决方案2】:

    这应该类似于其他最大组内和范围内加入的解决方案:

    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)
    

    【讨论】:

    • 刚刚在@JChao 的 SQLFiddle 中尝试过,但它并没有给出正确的结果(不返回所有项目)。我不确定我是否理解它是如何足以修复它的,你能看到什么吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-02-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-05
    • 1970-01-01
    相关资源
    最近更新 更多