按两列SQL Server分组时获取最新记录
我有一个跟踪版本历史记录的表。我只想获取最新版本以及表中每个id的日期。 下面将为我提供每个id、版本组合的最新日期。如何仅选择最上面的记录?我可以将其存储在临时表中,然后使用join只获取每个id、版本组合的顶部记录。是否有更好的方法在一个步骤中完成此操作 样本数据按两列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 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多分钟。