【问题标题】:SQL: Return rows where datetime of entry is most recent to specified timeSQL:返回条目的日期时间最近到指定时间的行
【发布时间】:2016-02-10 15:20:17
【问题描述】:

第一篇文章 - 为那个冗长的问题道歉。使用 SQLServer2012

这将更好地说明我的需要:

我有一个代理活动日志 - 每次他们在我们的呼叫中心更改电话状态时。每个用户每天可以有几个或数百个。我需要输入一个日期时间并让它返回每个人在该日期时间之前的状态。

目前,这是我的问题。这段代码给了我我想要的,但减去了我需要的一条信息:

SELECT        
MAX(StatusDateTime),
UserId

FROM            
AgentActivityLog 
WHERE
StatusDateTime BETWEEN '2/9/2016 00:00:00' AND '2/9/2016 08:52:12'

GROUP BY 
UserId

order by 2,1

这会返回一个用户 ID 列表,我可以在其中获得 2/9/2016 08:52:12 之前的最新条目。

2016-02-09 08:02:21.000 AbalosB
2016-02-09 08:06:12.000 AnkenbrD
2016-02-09 07:48:58.000 AzziV
2016-02-09 00:00:00.000 BachmayM

我的问题是我还需要包含另一列,其中包含他们所处状态的名称。当我这样做时,它包括该范围内的每个唯一时间;我假设是因为现在它为每个状态选择最长时间 - 而不仅仅是最长时间。

当我将 StatusKey 添加到 Select 和 Group By 时返回的示例:

2016-02-09 08:01:43.000 AbalosB     Gone Home
2016-02-09 08:02:21.000 AbalosB     Available
2016-02-09 08:00:50.000 AnkenbrD    Gone Home
2016-02-09 08:04:40.000 AnkenbrD    On ACD Call
2016-02-09 08:06:02.000 AnkenbrD    ACW - After Call Work
2016-02-09 08:06:12.000 AnkenbrD    Available

我需要包含该状态列,但我只需要在我输入时间之前首先出现的条目。这次我使用了一个范围,我可以输入 '2/9/2016 08:52:12' 并且 Id 需要该条目

【问题讨论】:

  • 哇,这么快就有这么多回复。让我试一试,我真的很感激你的回复。这将为我节省数小时繁琐的手工工作......

标签: sql sql-server datetime


【解决方案1】:

使用 RANK 和 CTE

WITH Data AS (
    SELECT
        StartDateTime,
        UserId,
        StatusKey,
        RANK() OVER(PARTITION BY UserId ORDER BY StartDateTime DESC) r
    FROM AgentActivityLog
    WHERE StatusDateTime BETWEEN '2/9/2016 00:00:00' AND '2/9/2016 08:52:12'

)
SELECT * FROM Data WHERE r = 1

【讨论】:

    【解决方案2】:

    如果你只需要一条记录,最近的一条,那么你可以使用TOP

    SELECT TOP 1 StatusDateTime, UserId, StatusKey
    FROM AgentActivityLog 
    WHERE StatusDateTime BETWEEN '2/9/2016 00:00:00' AND '2/9/2016 08:52:12'
    ORDER BY StatusDateTime DESC
    

    编辑:

    要获取每个用户的最新记录,请使用ROW_NUMBER

    SELECT StatusDateTime, UserId, StatusKey
    FROM (
      SELECT StatusDateTime, UserId, StatusKey, 
             ROW_NUMBER() OVER (PARTITION BY UserId 
                                ORDER BY StatusDateTime DESC) AS rn
      FROM AgentActivityLog 
      WHERE StatusDateTime BETWEEN '2/9/2016 00:00:00' AND '2/9/2016 08:52:12') t
    WHERE t.rn = 1
    

    【讨论】:

    • 漂亮 - 比我以前的任何东西都更快,更干净。非常感谢。从字面上看 - 节省了工作时间。
    • @Anthony 很高兴我能够提供帮助并欢迎来到 Stack Overflow。如果它帮助您解决问题,请将此或任何其他答案标记为已接受。
    【解决方案3】:

    假设您使用的是过去十年的 SQL Server 版本,您可以为此使用 Window 函数。您需要将其包含在 CTE 中,因为 Window 函数仅在 SELECTORDER BY 子句中可用:

    ;WITH MyCTE AS
    (
        SELECT
            UserId
            StartDateTime,
            StatusKey,
            ROW_NUMBER() OVER (PARTITION BY UserId ORDER BY StartDateTime) AS row_num
        FROM
            dbo.AgentActivityLog
        WHERE
            StatusDateTime BETWEEN '2/9/2016 00:00:00' AND '2/9/2016 08:52:12'
    )
    SELECT
        UserId,
        StartDateTime,
        StatusKey
    FROM
        MyCTE
    WHERE
        row_num = 1
    

    或者,您可以使用子查询:

    SELECT
        L.UserId,
        L.StartDateTime,
        L.StatusKey
    FROM
        dbo.AgentActivityLog L
    INNER JOIN
        (
            SELECT UserId, MAX(StartDateTime) AS MaxStartDateTime
            FROM dbo.AgentActivityLog
            WHERE StatusDateTime BETWEEN '2/9/2016 00:00:00' AND '2/9/2016 08:52:12'
            GROUP BY UserId
        ) SQ ON
            SQ.MaxStartDateTime = L.StartDateTime AND
            SQ.UserId = L.UserId
    

    这两种方法都假定您对于给定的UserIdStartDateTime 值没有完全相同的重复项。

    此外,您应该避免使用ORDER BY 2, 1(或任何列号)并使用实际的列/表达式。使用序数值是一个坏习惯,通常会导致代码出现错误和/或维护问题。

    【讨论】:

      猜你喜欢
      • 2020-05-10
      • 1970-01-01
      • 2015-02-08
      • 2013-04-01
      • 2019-08-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多