AJa*_*Jax 16 t-sql sql-server performance database-performance greatest-n-per-group
这是我发现自己的情景.
我有一个相当大的表,我需要查询来自的最新记录.以下是查询基本列的创建:
CREATE TABLE [dbo].[ChannelValue](
[ID] [bigint] IDENTITY(1,1) NOT NULL,
[UpdateRecord] [bit] NOT NULL,
[VehicleID] [int] NOT NULL,
[UnitID] [int] NOT NULL,
[RecordInsert] [datetime] NOT NULL,
[TimeStamp] [datetime] NOT NULL
) ON [PRIMARY]
GO
Run Code Online (Sandbox Code Playgroud)
ID列是主键,VehicleID和TimeStamp上有非Clustered索引
CREATE NONCLUSTERED INDEX [IX_ChannelValue_TimeStamp_VehicleID] ON [dbo].[ChannelValue]
(
[TimeStamp] ASC,
[VehicleID] ASC
)ON [PRIMARY]
GO
Run Code Online (Sandbox Code Playgroud)
我正在努力优化我的查询的表是超过2300万行,并且只是查询需要操作的大小的十分之一.
我需要为每个VehicleID返回最新的行.
我一直在查看StackOverflow上对这个问题的回答,我已经做了很多谷歌搜索,似乎有3或4种常见的方法在SQL Server 2005及更高版本上执行此操作.
到目前为止,我发现的最快的方法是以下查询:
SELECT cv.*
FROM ChannelValue cv
WHERE cv.TimeStamp = (
SELECT
MAX(TimeStamp)
FROM ChannelValue
WHERE ChannelValue.VehicleID = cv.VehicleID
)
Run Code Online (Sandbox Code Playgroud)
使用表中的当前数据量,执行大约需要6秒,这在合理的限制范围内,但是在实时环境中,表将包含的数据量开始执行得太慢.
查看执行计划,我关心的是SQL Server正在做什么来返回行.
我无法发布执行计划图像,因为我的声誉不够高,但索引扫描正在解析表中的每一行,这使得查询速度下降太多.

我尝试用几种不同的方法重写查询,包括使用SQL 2005 Partition方法,如下所示:
WITH cte
AS (
SELECT *,
ROW_NUMBER() OVER(PARTITION BY VehicleID ORDER BY TimeStamp DESC) AS seq
FROM ChannelValue
)
SELECT
VehicleID,
TimeStamp,
Col1
FROM cte
WHERE seq = 1
Run Code Online (Sandbox Code Playgroud)
但是,该查询的表现甚至更糟糕.
我尝试过重新构建这样的查询,但结果速度和查询执行计划几乎完全相同:
SELECT cv.*
FROM (
SELECT VehicleID
,MAX(TimeStamp) AS [TimeStamp]
FROM ChannelValue
GROUP BY VehicleID
) AS [q]
INNER JOIN ChannelValue cv
ON cv.VehicleID = q.VehicleID
AND cv.TimeStamp = q.TimeStamp
Run Code Online (Sandbox Code Playgroud)
我对表结构有一些灵活性(尽管程度有限),所以我可以为数据库添加索引,索引视图等甚至其他表.
我非常感谢这里的任何帮助.
编辑添加了执行计划图像的链接.
取决于您的数据(每组有多少行?)和您的索引.
有关3种方法的性能比较,请参阅优化TOP N每组查询.
在您的情况下,只有少量车辆有数百万行,我会添加索引VehicleID, Timestamp并执行
SELECT CA.*
FROM Vehicles V
CROSS APPLY (SELECT TOP 1 *
FROM ChannelValue CV
WHERE CV.VehicleID = V.VehicleID
ORDER BY TimeStamp DESC) CA
Run Code Online (Sandbox Code Playgroud)