sql-server – 选择忽略窄时间的行

我有一个表[EventLog]包含读取数据,由控制门的读卡器记录.但是,在读卡器附近持有一段时间的持卡人期间,可以多次读取相同的卡号[epc].

我想在同一个阅读器上显示相同代码的读取,但忽略读取2分钟,例如.

示例:EventLog

ID  EPC ReaderID    LogTime
1   1234    1   2016-04-15 12:33:55
2   1234    1   2016-04-15 12:34:05
3   1234    1   2016-04-15 12:34:10
4   4321    2   2016-04-15 12:34:12
5   4321    2   2016-04-15 12:34:14

期望的结果:

ID  EPC ReaderID    LogTime
1   1234    1   2016-04-15 12:33:55
4   4321    2   2016-04-15 12:34:12

我现在使用的是windows函数LAG,用于确定每次读取与前一次读取之间的分钟差异:

SELECT EPC, ReaderName, PersonName, LogTime
FROM (
SELECT EPC, ReaderName, PersonName, LogTime,
    DATEDIFF(MINUTE, LAG(LogTime) OVER (PARTITION BY EPC, ReaderID ORDER BY LogTime), LogTime) diff_prev
FROM EventLog l
    LEFT OUTER JOIN Person p ON p.EPC = l.EPC
    INNER JOIN Reader r ON r.ID = l.ReaderID
) tbl
WHERE diff_prev IS NULL OR diff_prev >= @ignoreMinutes
ORDER BY LogTime

其中@ignoreMinutes是一个参数,指定忽略相同读取的分钟数.

但是,如果卡每秒读取一次,持续3小时,则此解决方案不正确.例如:

ID  EPC ReaderID    LogTime     diff_prev
1   1234    1   2016-04-15 12:33:55     NULL
2   1234    1   2016-04-15 12:34:05     0
3   1234    1   2016-04-15 12:34:10     0
4   1234    1   2016-04-15 12:34:32     0
5   1234    1   2016-04-15 12:34:54     0
6   1234    1   2016-04-15 12:35:14     0
7   1234    1   2016-04-15 12:35:34     0
8   1234    1   2016-04-15 12:35:54     0
9   1234    1   2016-04-15 12:36:04     0
10  1234    1   2016-04-15 12:36:15     0
11   4321   2   2016-04-15 12:44:12     NULL
12   4321   2   2016-04-15 12:44:14     0

如你所见,我的解决方案在@ignoreMinutes = 1时执行,将导致只选择2行ID = 1,11,因为其余的都是diff_prev = 0.但是正确的结果集应该是ID = 1,6,10, 11

你能帮我吗?谢谢!

最佳答案 这是我提出的“候选人”解决方案.至少它在你的上一个例子中正常工作,返回记录1,6,10,11.

DECLARE @intervalSeconds INT
SET @intervalSeconds = 60;

WITH EL AS
(
    -- Select first record for each EPC, this is the baseline for recursion
    SELECT
        ID,
        EPC,
        LogTime
    FROM EventLog
    WHERE LogTime = (SELECT MIN(LogTime) FROM EventLog IEL WHERE IEL.EPC = EventLog.EPC)
    -- Add following events
    UNION ALL
    SELECT
        ID,
        EPC,
        LogTime
    FROM
    (
        SELECT
            NextEvent.ID,
            NextEvent.EPC,
            NextEvent.LogTime,
            ROW_NUMBER() OVER(PARTITION BY NextEvent.EPC ORDER BY NextEvent.LogTime) eventNumber
        FROM EventLog NextEvent
        JOIN
        (
            SELECT 
                ID,
                ROW_NUMBER() OVER(PARTITION BY EPC ORDER BY LogTime DESC) eventNumber, -- Reverse numbering to get last row by readNumber = 1
                EPC,
                LogTime
            FROM EL -- Recursion
        ) PreviousEvent -- Here we have all already selected events wich we're interested in
            ON PreviousEvent.EPC = NextEvent.EPC
                AND PreviousEvent.eventNumber = 1 -- We need only the last one for each EPC
        WHERE DATEDIFF(SECOND, PreviousEvent.LogTime, NextEvent.LogTime) > @intervalSeconds

    ) NextCandidateEvents -- Here we have all events with desired interval offset for each EPC
    WHERE NextCandidateEvents.eventNumber = 1 -- We need only the first one for each EPC
)
SELECT * FROM EL
ORDER BY EPC, LogTime
点赞