繁体   English   中英

Microsoft SQL Server:仅返回每个唯一ID具有最新日期的行

[英]Microsoft SQL Server : return only the rows with the most recent date for each unique ID

我有一些具有DeviceID列,扫描时间列和其他一些列的数据。

对于每个deviceID,我只想根据扫描时间返回最近的行。

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

该数据库是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-仅返回最近的行

但是似乎无法使他们工作。 我不确定这是我做错了还是我使用的特定品牌的SQL没有执行“ top 1”操作。

有什么方法可以做我想要的吗? 我和我的财产有多近?

使用带有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 

检查一下:

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)

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

  • 在您的子查询中添加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
    )

如果你有你的表自动递增列(你一般应该有一个对每个表),用这个来代替时间戳,因为SQL服务器DateTime类型只有一个1/300 第二的分辨率和不应假设成为唯一的时间戳。

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填充数据

最后一个选择,因为我没有提到

您可以将WITH TIESRow_Number()配合使用

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM