按两列SQL Server分组时获取最新记录

按两列SQL Server分组时获取最新记录,sql,sql-server-2012,Sql,Sql Server 2012,我有一个跟踪版本历史记录的表。我只想获取最新版本以及表中每个id的日期。 下面将为我提供每个id、版本组合的最新日期。如何仅选择最上面的记录?我可以将其存储在临时表中,然后使用join只获取每个id、版本组合的顶部记录。是否有更好的方法在一个步骤中完成此操作 样本数据 id version timestamp 123 1.5 2015-03-28 08:21:04.563 123 1.0 2015-03-21 12:58:24.730 234 1.5

我有一个跟踪版本历史记录的表。我只想获取最新版本以及表中每个id的日期。 下面将为我提供每个id、版本组合的最新日期。如何仅选择最上面的记录?我可以将其存储在临时表中,然后使用join只获取每个id、版本组合的顶部记录。是否有更好的方法在一个步骤中完成此操作

样本数据

id  version     timestamp
123 1.5         2015-03-28 08:21:04.563
123 1.0         2015-03-21 12:58:24.730
234 1.5         2016-10-15 23:08:09.550
345 1.5         2016-05-10 15:18:09.707
345 1.5         2016-09-02 21:30:00.657
预期产量

id  version     timestamp
123 1.5         2015-03-28 08:21:04.563
234 1.5         2016-10-15 23:08:09.550
345 1.5         2016-09-02 21:30:00.657
质疑

我已经试过了,但是我得到了和上面一样的结果

select * from 
( 
    select  id,version,dt_create,row_number() over (partition by id,version order by dt_create desc) as a
    from version_history (nolock) 
) b
where a=1
order by id

您可以使用WITH TIES子句

示例

Declare @YourTable Table ([id] int,[version] varchar(50),[timestamp] datetime)
Insert Into @YourTable Values 
 (123,1.5,'2015-03-28 08:21:04.563')
,(123,1.0,'2015-03-21 12:58:24.730')
,(234,1.5,'2016-10-15 23:08:09.550')
,(345,1.5,'2016-05-10 15:18:09.707')
,(345,1.5,'2016-09-02 21:30:00.657')

Select Top 1 with ties *
 From  @YourTable
 Order By Row_Number() over (Partition By ID Order By timestamp Desc)
返回

id  version timestamp
123 1.5     2015-03-28 08:21:04.563
234 1.5     2016-10-15 23:08:09.550
345 1.5     2016-09-02 21:30:00.657

您可以使用WITH TIES子句

示例

Declare @YourTable Table ([id] int,[version] varchar(50),[timestamp] datetime)
Insert Into @YourTable Values 
 (123,1.5,'2015-03-28 08:21:04.563')
,(123,1.0,'2015-03-21 12:58:24.730')
,(234,1.5,'2016-10-15 23:08:09.550')
,(345,1.5,'2016-05-10 15:18:09.707')
,(345,1.5,'2016-09-02 21:30:00.657')

Select Top 1 with ties *
 From  @YourTable
 Order By Row_Number() over (Partition By ID Order By timestamp Desc)
返回

id  version timestamp
123 1.5     2015-03-28 08:21:04.563
234 1.5     2016-10-15 23:08:09.550
345 1.5     2016-09-02 21:30:00.657

我从partition子句中删除version之后,第二个查询就可以工作了

select * from 
( 
    select  id,version,dt_create,row_number() over (partition by id order by dt_create desc) as a
    from version_history (nolock) 
) b
where a=1
order by id

我从partition子句中删除version之后,第二个查询就可以工作了

select * from 
( 
    select  id,version,dt_create,row_number() over (partition by id order by dt_create desc) as a
    from version_history (nolock) 
) b
where a=1
order by id

我看到您可以通过分区来实现这一点,但是我想向您展示另一种方法。您可以自己比较性能,看看什么是最快的(我假设分区更好)

首先,我们知道,即使是最新版本,也可能有多个日期,因此您确实不希望每个id的最大版本,而是希望最大日期

首先,设置:

DECLARE @table TABLE (ID INT, [Version] DECIMAL(18, 2), [TimeStamp] DATETIME)

INSERT INTO @table
VALUES
(123, 1.5, '2015-03-28 08:21:04.563'),
(123, 1.5, '2015-03-21 12:58:24.730'),
(234, 1.5, '2016-10-15 23:08:09.550'),
(345, 1.5, '2016-05-10 15:18:09.707'),
(345, 1.5, '2016-09-02 21:30:00.657')
现在,要获取每个ID的最大日期:

SELECT ID, 
       MAX([TimeStamp]) AS MaxTimeStamp
FROM @table
GROUP BY ID
这给了我们想要的:

ID          MaxTimeStamp
----------- -----------------------
123         2015-03-28 08:21:04.563
234         2016-10-15 23:08:09.550
345         2016-09-02 21:30:00.657

(3 row(s) affected)
现在我们只需要包含这个版本。这应该很容易,因为我们可以在ID和日期上进行自连接:

SELECT T.ID, 
       MAX(T.[TimeStamp]) AS [MaxTimeStamp], 
       T2.[Version] AS [MaxVersion]
FROM @table T
JOIN @table T2
    ON T.ID = T2.ID
    AND T.[TimeStamp] = T2.[TimeStamp]
GROUP BY T.ID, T2.[Version]
这给了我们以下结果:

ID          MaxTimeStamp            MaxVersion
----------- ----------------------- ---------------------------------------
123         2015-03-28 08:21:04.563 1.50
234         2016-10-15 23:08:09.550 1.50
345         2016-09-02 21:30:00.657 1.50

(3 row(s) affected)
请注意,我们将原始查询连接到同一个表-按ID和日期,如前所述。但是,我们现在选择了一个附加的列,Version,因此它需要包含在
组中

或者,您可以通过使用
交叉应用
,获得相同的结果:

SELECT  T.ID, 
        MAX(T.[TimeStamp]) AS MaxTimeStamp, 
        T2.[Version]
FROM @table T
CROSS APPLY
    (
        SELECT Version 
        FROM @table T2
        WHERE T2.ID = T.ID  
        AND T2.[TimeStamp] = T.[TimeStamp]
    ) T2
GROUP BY T.ID, T2.[Version]

我看到您可以通过分区来实现这一点,但是我想向您展示另一种方法。您可以自己比较性能,看看什么是最快的(我假设分区更好)

首先,我们知道,即使是最新版本,也可能有多个日期,因此您确实不希望每个id的最大版本,而是希望最大日期

首先,设置:

DECLARE @table TABLE (ID INT, [Version] DECIMAL(18, 2), [TimeStamp] DATETIME)

INSERT INTO @table
VALUES
(123, 1.5, '2015-03-28 08:21:04.563'),
(123, 1.5, '2015-03-21 12:58:24.730'),
(234, 1.5, '2016-10-15 23:08:09.550'),
(345, 1.5, '2016-05-10 15:18:09.707'),
(345, 1.5, '2016-09-02 21:30:00.657')
现在,要获取每个ID的最大日期:

SELECT ID, 
       MAX([TimeStamp]) AS MaxTimeStamp
FROM @table
GROUP BY ID
这给了我们想要的:

ID          MaxTimeStamp
----------- -----------------------
123         2015-03-28 08:21:04.563
234         2016-10-15 23:08:09.550
345         2016-09-02 21:30:00.657

(3 row(s) affected)
现在我们只需要包含这个版本。这应该很容易,因为我们可以在ID和日期上进行自连接:

SELECT T.ID, 
       MAX(T.[TimeStamp]) AS [MaxTimeStamp], 
       T2.[Version] AS [MaxVersion]
FROM @table T
JOIN @table T2
    ON T.ID = T2.ID
    AND T.[TimeStamp] = T2.[TimeStamp]
GROUP BY T.ID, T2.[Version]
这给了我们以下结果:

ID          MaxTimeStamp            MaxVersion
----------- ----------------------- ---------------------------------------
123         2015-03-28 08:21:04.563 1.50
234         2016-10-15 23:08:09.550 1.50
345         2016-09-02 21:30:00.657 1.50

(3 row(s) affected)
请注意,我们将原始查询连接到同一个表-按ID和日期,如前所述。但是,我们现在选择了一个附加的列,Version,因此它需要包含在
组中

或者,您可以通过使用
交叉应用
,获得相同的结果:

SELECT  T.ID, 
        MAX(T.[TimeStamp]) AS MaxTimeStamp, 
        T2.[Version]
FROM @table T
CROSS APPLY
    (
        SELECT Version 
        FROM @table T2
        WHERE T2.ID = T.ID  
        AND T2.[TimeStamp] = T.[TimeStamp]
    ) T2
GROUP BY T.ID, T2.[Version]

以上所有答案并没有真正选择最新的两列。只有1列最大时间戳

如果以前的版本可以具有更高版本的更晚构建日期(如安全错误修复),则安装可能如下所示:

DECLARE @table TABLE (ID INT, [Version] DECIMAL(18, 2), [TimeStamp] DATETIME)

INSERT INTO @table
VALUES
 (123,1.5,'2015-03-28 08:21:04.563')
,(123,1.0,'2015-03-21 12:58:24.730')
,(123,1.0,'2016-03-21 12:58:24.730') --new
,(234,1.5,'2016-10-15 23:08:09.550')
,(345,1.5,'2016-05-10 15:18:09.707')
,(345,1.5,'2016-09-02 21:30:00.657')
之前的答案将导致4条记录,包括ID 123和2016年日期的1.0版本。 如果这不是目的,那么这就是解决方案:

SELECT T.ID, 
       MAX(T.[TimeStamp]) AS [MaxTimeStamp], 
       T3.[Version] AS [MaxVersion]
FROM @table T
JOIN (
        SELECT T4.ID, 
           MAX(T4.[Version]) AS [MaxVersion]
        FROM @table T4
        GROUP BY T4.ID
    ) as T2 ON T.id = T2.id
JOIN @table T3
    ON T.ID = T3.ID
    --AND T2.ID = T3.ID --works fine somehow with or without thise line
    AND T.[TimeStamp] = T3.[TimeStamp]
    AND T3.[Version] = T2.[MaxVersion]
GROUP BY T.ID, T3.[Version]

我需要一个额外的连接,但我想知道是否没有额外的连接就可以了。

以上所有答案都没有真正选择最新的两列。只有1列最大时间戳

如果以前的版本可以具有更高版本的更晚构建日期(如安全错误修复),则安装可能如下所示:

DECLARE @table TABLE (ID INT, [Version] DECIMAL(18, 2), [TimeStamp] DATETIME)

INSERT INTO @table
VALUES
 (123,1.5,'2015-03-28 08:21:04.563')
,(123,1.0,'2015-03-21 12:58:24.730')
,(123,1.0,'2016-03-21 12:58:24.730') --new
,(234,1.5,'2016-10-15 23:08:09.550')
,(345,1.5,'2016-05-10 15:18:09.707')
,(345,1.5,'2016-09-02 21:30:00.657')
之前的答案将导致4条记录,包括ID 123和2016年日期的1.0版本。 如果这不是目的,那么这就是解决方案:

SELECT T.ID, 
       MAX(T.[TimeStamp]) AS [MaxTimeStamp], 
       T3.[Version] AS [MaxVersion]
FROM @table T
JOIN (
        SELECT T4.ID, 
           MAX(T4.[Version]) AS [MaxVersion]
        FROM @table T4
        GROUP BY T4.ID
    ) as T2 ON T.id = T2.id
JOIN @table T3
    ON T.ID = T3.ID
    --AND T2.ID = T3.ID --works fine somehow with or without thise line
    AND T.[TimeStamp] = T3.[TimeStamp]
    AND T3.[Version] = T2.[MaxVersion]
GROUP BY T.ID, T3.[Version]

我需要一个额外的连接,但我不知道是否可以不使用额外的连接。

谢谢。我的表有2.1亿条记录,这需要40多分钟。使用连接,我可以在8分钟内返回结果。@好奇的\u很高兴你得到了一个解决方案。除非将数据加载到表变量中,否则无法想象为什么会有40多分钟。谢谢。我的表有2.1亿条记录,这需要40多分钟。使用join,我可以在8分钟内返回结果。@好奇的人很高兴你有了解决方案。无法想象,除非将数据加载到表变量中,否则为什么会有40多分钟。