17

这是我发现自己所处的场景。

我有一个相当大的表,我需要从中查询最新记录。这是查询的基本列的创建:

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

ID 列是主键,并且 VehicleID 和 TimeStamp 上有一个非聚集索引

CREATE NONCLUSTERED INDEX [IX_ChannelValue_TimeStamp_VehicleID] ON [dbo].[ChannelValue] 
(
    [TimeStamp] ASC,
    [VehicleID] ASC
)ON [PRIMARY]
GO

我正在优化我的查询的表有超过 2300 万行,并且只是查询需要操作的大小的十分之一。

我需要返回每个 VehicleID 的最新行。

我一直在 StackOverflow 上查看对这个问题的回答,并且我已经做了一些谷歌搜索,在 SQL Server 2005 及更高版本上似乎有 3 或 4 种常见的方法来执行此操作。

到目前为止,我发现的最快方法是以下查询:

SELECT cv.*
FROM ChannelValue cv
WHERE cv.TimeStamp = (
SELECT
    MAX(TimeStamp)
FROM ChannelValue
WHERE ChannelValue.VehicleID = cv.VehicleID
)

使用表中的当前数据量,执行大约需要 6 秒,这在合理的范围内,但是随着表将包含在实时环境中的数据量,查询开始执行太慢。

查看执行计划,我关心的是 SQL Server 为返回行所做的工作。

我无法发布执行计划图像,因为我的 Reputation 不够高,但索引扫描正在解析表中的每一行,这大大减慢了查询速度。

执行计划

我尝试使用几种不同的方法重写查询,包括使用 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

但是该查询的性能甚至差很多。

我尝试过像这样重新构建查询,但结果速度和查询执行计划几乎相同:

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

我在表结构方面有一些灵活性(尽管程度有限),所以我可以向数据库添加索引、索引视图等,甚至可以添加额外的表。

我将非常感谢这里的任何帮助。

编辑添加了执行计划图像的链接。

4

3 回答 3

7

取决于您的数据(每组有多少行?)和您的索引。

有关3 种方法的一些性能比较,请参阅优化每组查询的前 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  
于 2011-09-22T13:19:36.643 回答
0

如果您的记录是按顺序插入的,则TimeStamp在查询中替换为ID可能会有所不同。

作为旁注,这返回了多少条记录?如果您要返回数十万行,您的延迟可能是网络开销。

于 2011-09-22T13:28:28.870 回答
0

试试这个:

SELECT SequencedChannelValue.* -- Specify only the columns you need, exclude the SequencedChannelValue
FROM
    (
        SELECT 
            ChannelValue.*,   -- Specify only the columns you need
            SeqValue = ROW_NUMBER() OVER(PARTITION BY VehicleID ORDER BY TimeStamp DESC)
        FROM ChannelValue
    ) AS SequencedChannelValue
WHERE SequencedChannelValue.SeqValue = 1

需要进行表或索引扫描,因为您没有以任何方式过滤数据。您要求所有 VehicleID 的最新时间戳 - 查询引擎必须查看每一行以找到最新的时间戳。

您可以通过缩小返回的列数(不要使用 SELECT *)以及提供由 VehicleID + TimeStamp 组成的索引来帮助解决此问题。

于 2011-09-22T13:32:43.440 回答