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

Microsoft SQL Server:仅返回具有每个唯一ID的最新日期的行,sql,sql-server,sql-server-2012,Sql,Sql Server,Sql Server 2012,我有一些数据有一个DeviceID列,一个扫描时间列和一些其他列 对于每个DeviceID,我只想根据扫描时间返回最近的一行 我正在尝试创建此查询,以便将其用作数据的视图和报告 该数据库是Microsoft SQL Server数据库,我正在运行SQL Server 2014 Management Studio中的查询 我能做到的最接近于让它发挥作用的是: SELECT DeviceID, AVSolutionName, DefinitionsUpToDate,

我有一些数据有一个
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没有做到“第一名”

有没有办法做到我想要的?我与我所拥有的有多接近?

检查以下内容:

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)
的行,请检查以下内容:

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

  • 不需要在子句中使用
    来匹配子查询,等式应该是好的,因为无论如何只需要一条记录

  • 无需使用
    DISTINCT
    ,因为您已经在使用
    分组依据

查询:

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
    )

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

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

  • 不需要在子句中使用
    来匹配子查询,等式应该是好的,因为无论如何只需要一条记录

  • 无需使用
    DISTINCT
    ,因为您已经在使用
    分组依据

查询:

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
    )

是否将窗口功能与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 

是否将窗口功能与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 

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

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

这假定您不使用

对数据进行回溯或填充。如果您的表上有一个自动递增列(通常每个表上都应该有一个),请使用该列而不是时间戳,因为SQL Server
DateTime
类型只有一个时间戳,不应假定它是唯一的时间戳

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

这假定您不会使用最后一个选项回溯数据或填充数据,因为我没有看到提到它

您可以将带领带的
行编号()配合使用

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

示例

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

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

您可以将带领带的
行编号()配合使用

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

示例

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