优化 SQL Server 中的简单查询

Hau*_*uri 2 performance sql-server query-performance

这应该是一个非常简单的查询,但老实说,我认为它的执行时间可以改进。

select idTag,MAX(pctimestamp) AS PCTIMESTAMP,getdate() AS NOW, datediff(SECOND,MAX(pctimestamp),getdate()) AS DELAY 
from ValuesTagsOPC
group by IdTag
Run Code Online (Sandbox Code Playgroud)

此查询从“ValuesTagsOPC”表返回 1386 行,该表包含大约 4000 万行,并具有以下结构,由创建脚本检索:

CREATE TABLE [dbo].[ValuesTagsOPC](
    [IdTag] [int] NOT NULL,
    [TTimeStamp] [datetime] NOT NULL,
    [PCTimeStamp] [datetime] NOT NULL,
    [Value] [nvarchar](50) NOT NULL,
    [Quality] [int] NOT NULL,
 CONSTRAINT [PK_ValuesTagsOPC] PRIMARY KEY CLUSTERED 
(
    [IdTag] ASC,
    [PCTimeStamp] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]
Run Code Online (Sandbox Code Playgroud)

很明显,它的主键上有一个聚集索引。SQL Server 的时间和 IO 统计信息如下(抱歉,它是西班牙语):

(1386 filas afectadas)
Tabla 'ValuesTagsOPC'. Recuento de exámenes 5, lecturas lógicas 224612, lecturas físicas 0, lecturas anticipadas 0, lecturas lógicas de LOB 0, lecturas físicas de LOB 0, lecturas anticipadas de LOB 0.

 Tiempos de ejecución de SQL Server:
   Tiempo de CPU = 9578 ms, tiempo transcurrido = 3019 ms.
Run Code Online (Sandbox Code Playgroud)

我检查了估计的执行计划与真实的执行计划相同,它告诉我 94% 的成本来自集群索引扫描。我不明白的是为什么它不执行搜索而不是扫描,因为查询中的所有必填字段都包含在聚集索引中....

提前致谢!!

Mar*_*ith 6

它没有WHERE子句,所以它必须处理和聚合所有 4000 万行。SQL Server 将不会利用索引顺序并IdTag在找到MAX当前组的后跳过扫描到下一个,但将继续处理该组中的其他行。每个组平均有大约 30,000 行。

由于您有另一个表列出了 1,386 种不同的 IdTag 类型,因此您可以尝试以下操作。

SELECT D.IdTag,
       V.PCTimeStamp,
       V.Now,
       datediff(SECOND, V.PCTimeStamp, V.Now) AS DELAY
FROM   DescriptionTagsOPC D
       CROSS APPLY (SELECT TOP 1 *,
                                 getdate() AS Now
                    FROM   ValuesTagsOPC V
                    WHERE  D.IdTag = V.IdTag
                    ORDER  BY PCTimeStamp DESC) V 
Run Code Online (Sandbox Code Playgroud)

用 1,386 次搜索替换 4000 万行的扫描。

如果该表不可用,则可以使用递归 CTE 来实现类似的结果。

WITH    RecursiveCTE
AS      (
        SELECT TOP 1 IdTag, PCTimeStamp
        FROM ValuesTagsOPC
        ORDER BY IdTag DESC, PCTimeStamp DESC
        UNION   ALL
        SELECT  R.IdTag, R.PCTimeStamp
        FROM    (
                SELECT  V.*,
                        rn = ROW_NUMBER() OVER (ORDER BY V.IdTag DESC, V.PCTimeStamp DESC)
                FROM    ValuesTagsOPC V
                JOIN    RecursiveCTE R
                        ON  V.IdTag < R.IdTag
                ) R
        WHERE   R.rn = 1
        )
SELECT  IdTag,
        PCTimeStamp,
        getdate()                                 AS NOW,
        datediff(SECOND, PCTimeStamp, getdate()) AS DELAY
FROM    RecursiveCTE
OPTION  (MAXRECURSION 0);
Run Code Online (Sandbox Code Playgroud)