使用 Inner Join 确定表中特定记录和相关事件之间的间隔

Posted

技术标签:

【中文标题】使用 Inner Join 确定表中特定记录和相关事件之间的间隔【英文标题】:Using Inner Join to determine the interval between specific records and related events in a table 【发布时间】:2012-05-07 03:40:11 【问题描述】:

我有一个简单的表格,用于记录针对特定访问的事件:

Describe Histories;
    +------------------+
    | Field            |
    +------------------+
    | HistoryId        |
    | VisitId          |
    | Location         |
    | Event            |
    | EventTime        |
    +------------------+

个人与访问 (VisitId) 相关联。对于每次访问,一个人可能有多个历史记录。事件可以是入学、转学或出院。

我正在尝试编写一个查询来计算每个人在每个位置的持续时间。请注意,他们可能会在每次访问中多次访问某个位置。个人通过入场或转移事件进入位置,并通过出院或转移离开。

如果个人进入地点“A”,他们的入院或转学记录将列出地点“A”,但如果他们转出(或出院),则会列出另一个地点,例如“B”。

因此,我必须找出转移到位置“A”和随后(及时)转移到位置“B”之间的时间间隔。不评估内部位置转移。

我知道该解决方案可能基于 INNER JOIN,但是我不知道如何选择与最近的“输入”相对应的“输出”记录。

我想这相当复杂 - 我希望我的解释足够清楚。

非常感谢任何指导。

【问题讨论】:

【参考方案1】:

假设转移或出院是一个独特的事件,你可以这样写

SELECT
   b.EventTime - a.EventTime        
FROM
   Histories a
   INNER JOIN Histories b
   ON a.VisitID = b.VisitID
WHERE
   a.event = 'Admission'
   and
   b.event in ('Transfer', 'Discharge')

如果您对上次转移或出院感兴趣,您会写信

SELECT
   b.EventTime - a.EventTime        
FROM
   Histories a
   INNER JOIN  Histories b
    ON a.VisitID = b.VisitID

   INNER JOIN 
   (SELECT
         VisitId, 
         MAX(HistoryID) HistoryID
    FROM Histories 
    WHERE 
       b.event in ('Transfer', 'Discharge')
    GROUP BY 
       VisitId) maxHistory
   ON b.HistoryID = maxHistoryId.HistoryId

WHERE
   a.event = 'Admission'

但是,如果一次访问可能会导致多次访问,因为 Andriy M 提到您有间隙和岛屿问题(特别是岛屿)

在这种情况下,您需要以下内容

SELECT  
       a.VisitId,
       a.Event a_Event, 
       a.Event b_Event, 
       a.EventTime a_EventTime,
       b.EventTime b_EventTime,
       b_EventTime - a_EventTime

FROM   histories a 
       INNER JOIN histories B 
         ON a.visitID = b.visitID 
            AND a.EventTime < b.eventTime 
       INNER JOIN (SELECT a.VisitId, 
                          a.EventTime      a_EventTime, 
                          Min(b.EventTime) b_EventTime 
                   FROM   histories a 
                          INNER JOIN histories B 
                            ON a.visitID = b.visitID 
                               AND a.EventTime < b.eventTime 
                   GROUP  BY a_EventTime, 
                             a.VisitId) MinTime 
         ON a.VisitID = MinTime.VisitID 
            AND a.EventTime = a_EventTime 
            AND b.EventTime = b_EventTime 

DEMO

使用以下示例数据

CREATE TABLE Histories 
    (
     HistoryId int auto_increment primary key, 
     VisitId int,
     Location varchar(20),
     Event varchar(20), 
     EventTime datetime
    );

INSERT INTO Histories
(VisitId, Location, Event, EventTime)
VALUES
(1, 'A', 'Admission', '2012-01-01'),
(1, 'A', 'Discharge', '2012-01-03'),
(2, 'B', 'Admission', '2012-01-02'),
(2, 'C', 'Transfer', '2012-01-05'),
(2, 'C', 'Discharge', '2012-01-06'),
(3, 'D', 'Admission', '2012-01-06'),
(3, 'E', 'Transfer', '2012-01-07'),
(3, 'F', 'Transfer', '2012-01-08'),
(3, 'F', 'Discharge', '2012-01-10');

你得到以下结果

VISITID    A_EVENT   B_EVENT    A_EVENTTIME                     B_EVENTTIME                     B_EVENTTIME - A_EVENTTIME
1          Admission Discharge  January, 01 2012 00:00:00-0800  January, 03 2012 00:00:00-0800  2000000
2          Admission Transfer   January, 02 2012 00:00:00-0800  January, 05 2012 00:00:00-0800  3000000
2          Transfer  Discharge  January, 05 2012 00:00:00-0800  January, 06 2012 00:00:00-0800  1000000
3          Admission Transfer   January, 06 2012 00:00:00-0800  January, 07 2012 00:00:00-0800  1000000
3          Transfer  Transfer   January, 07 2012 00:00:00-0800  January, 08 2012 00:00:00-0800  1000000
3          Transfer  Discharge  January, 08 2012 00:00:00-0800  January, 10 2012 00:00:00-0800  2000000

注意事项:

这假设您不关心尚未有相应出院/转院的入院/转院。 如果您知道在输入记录后 eventTime 不会改变,您可以使用 historyID 而不是 eventtime 来确定事件的顺序。 您知道如何以您喜欢的格式获取事件时差

【讨论】:

谢谢你。第一个例子很有帮助,虽然第二个更适合我正在寻找的结构。我认为我仍然在两个方面苦苦挣扎。主要的是我需要将入学或转移到一个位置与下一次转移或排出相关联,而不是您示例中的最近一次。一个人可能在一个位置有多个“进/出”事件。 @bugy: 同一个VisitId可以多次转移到同一个位置吗? 是的。个人可以访问“诊所”。访问诊所与访问 ID 相关联。然而,在访问期间,他们可能会从一个房间移动到另一个房间,也可能会再次回到他们已经去过的房间。每次他们进入一个房间时,都会创建一个历史记录(入场或转移)。每次他们离开时都会创建历史记录(转移或放电)。问题是确定特定房间的停留时间。这归结为“in”事件和随后的“out”事件之间的时间差异。 @bugy:我在您的帖子中添加了gaps-and-islands 标签,因为您的问题似乎归结为这种问题。在同一个 SQL 产品中与同一个问题相关的 SO 问题并不多,但您可能仍想have a look at them,因为其中一些包含已接受的答案。 @AndriyM 当我写下我的初始答案时,我希望这不是一个 g&i 问题。啊,太好了【参考方案2】:

这对你有什么作用?

SELECT 
    h1.HistoryId, 
    h1.VisitId, 
    h1.Event AS InitialEvent, 
    h2.Event AS FinalEvent, 
    h1.Location AS StartLocation,
    h2.Location AS EndLocation,
    IF(h2.HistoryId, UNIX_TIMESTAMP(h2.EventTime) - UNIX_TIMESTAMP(h1.EventTime), NULL) AS transfer_duration_seconds
FROM Histories h1 
LEFT JOIN Histories h2 ON h1.VisitId = h2.VisitId AND h1.Location != h2.location AND h2. EventTime > h1. EventTime
GROUP BY h1.VisitId

【讨论】:

谢谢你 Bryan - 这也是有道理的。但是,与 COnrad 一样,我正在努力将 InitialEvent 与 SubsequentEvent 联系起来 - 每个 VisitId 可能有多个配对。 我有点困惑,我的查询应该从历史中获取每个事件,并根据 EventTime 加入下一个连续事件。它还在做其他事情吗?

以上是关于使用 Inner Join 确定表中特定记录和相关事件之间的间隔的主要内容,如果未能解决你的问题,请参考以下文章

SQL语句(inner join,left out join,right out join三者的不同

inner join 还是 left join 什么区别啊

SQL left joinright join和inner join的区别以及where的搭配使用

inner join和left join right join 的区别?

SQLServer中 join 跟inner join的区别是啥?

left joinright join和inner join