【问题标题】:Microsoft SQL Server : return only the rows with the most recent date for each unique IDMicrosoft SQL Server:仅返回每个唯一 ID 具有最近日期的行
【发布时间】:2019-05-11 20:56:30
【问题描述】:

我有一些数据包含 DeviceID 列、扫描时间列和其他一些列。

对于每个设备 ID,我只想根据扫描时间返回最新的行。

我正在尝试创建此查询,以便可以将其用作数据视图和报告。

该数据库是 Microsoft SQL Server 数据库,我正在从 SQL Server 2014 Management Studio 运行查询。

我最接近让它工作的是:

SELECT
    DeviceID,
    AVSolutionName, 
    DefinitionsUpToDate,
    ScanningEnabled,
    Expired, 
    ScanTime
FROM 
    dbo.fact_AVSecurity
WHERE 
    (ScanTime IN (SELECT DISTINCT MAX(ScanTime) AS LastScan
                  FROM dbo.fact_AVSecurity AS Avs
                  GROUP BY DeviceID))

不幸的是,这会为同一个 ID 返回多个值。

ScanTime        ScanningEnabled Expired    DeviceID DefinitionsUpToDate   AVSolutionName
10/12/2018 10:13    TRUE             FALSE      15994             TRUE    Webroot SecureAnywhere
4/12/2018 14:30     TRUE             TRUE       15994             TRUE    Webroot SecureAnywhere

我想要返回的只是最近的第一行:

ScanTime        ScanningEnabled Expired    DeviceID DefinitionsUpToDate   AVSolutionName
10/12/2018 10:13    TRUE             FALSE      15994             TRUE       Webroot SecureAnywhere

我尝试了不同的方法,例如: SQL - Returning only the most recent row

但似乎无法让它们工作。我不确定这是否是我做错了,或者我使用的特定品牌的 SQL 没有做到“前 1”的事情。

有没有办法做我所追求的?我与我所拥有的有多接近?

【问题讨论】:

    标签: sql sql-server sql-server-2012


    【解决方案1】:

    检查一下:

    SELECT t.DeviceID, t.AVSolutionName, t.DefinitionsUpToDate, t.ScanningEnabled, t.Expired, t.ScanTime
    FROM dbo.fact_AVSecurity AS t
    WHERE t.ScanTime = (SELECT MAX(Avs.ScanTime) FROM dbo.fact_AVSecurity AS Avs WHERE Avs.DeviceID = t.DeviceID)
    

    对于每个DeviceID 获取具有ScanTime = MAX(ScanTime) 的行

    【讨论】:

      【解决方案2】:

      您已接近解决方案。您只需要对相关子查询进行一些更改:

      • 在子查询中添加 WHERE 条件,将搜索限制为当前的 DeviceID

      • 无需使用IN 子句来匹配子查询,相等应该没问题,因为无论如何只需要一条记录

      • 无需使用DISTINCT,因为您已经在使用GROUP BY

      查询:

      SELECT 
          t.DeviceID, 
          t.AVSolutionName,
          t.DefinitionsUpToDate, 
          t.ScanningEnabled,
          t.Expired,
          t.ScanTime
      FROM dbo.fact_AVSecurity AS t
      WHERE t.ScanTime =
          (SELECT MAX(ScanTime) AS LastScan
            FROM dbo.fact_AVSecurity AS Avs
            WHERE deviceID = t.deviceID
            GROUP BY DeviceID
          )
      

      【讨论】:

        【解决方案3】:

        在 CTE 中使用窗口函数?

        With CTE AS (
        SELECT t.DeviceID
             , t.AVSolutionName
             , t.DefinitionsUpToDate
             , t.ScanningEnabled
             , t.Expired
             , t.ScanTime
             , Row_Number() over (partition by DeviceID order by scanTime Desc) RN
        FROM dbo.fact_AVSecurity t)
        SELECT * 
        FROM CTE 
        WHERE RN=1 
        

        【讨论】:

          【解决方案4】:

          如果您的表上有一个自动增量列(您通常应该在 每个 表上都有一个),请使用它而不是时间戳,因为 SQL Server DateTime 类型只有一个 @987654321 @ 并且不应被假定为唯一的时间戳。

          SELECT X.LastEntryID, DeviceID = Y.ID, ...
          FROM
              (
                  SELECT LastEntryID = MAX(ID)--latest entry for the device
                  FROM dbo.fact_AVSecurity
                  GROUP BY DeviceID--you don't even need to return DeviceID since ID is auto-increment and thus unique in the table
              ) AS X
              INNER JOIN dbo.fact_AVSecurity AS Y ON
                  Y.ID = X.LastEntryID
          

          这假定您没有回溯数据或使用 IDENTITY_INSERT 填充数据

          【讨论】:

            【解决方案5】:

            最后一个选项,因为我没有看到它提到它

            您可以将WITH TIES 与Row_Number()

            结合使用

            也就是说,xQbert 的解决方案 (+1) 会更高效,尤其是对于较大的表

            示例

            SELECT Top 1 with ties *
             FROM  dbo.fact_AVSecurity 
             Order By Row_Number() over (partition by DeviceID order by scanTime Desc) 
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2023-03-03
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多