用于比较表中日期列的 SQL Server 索引策略

问题描述 投票:0回答:2

我有一个带有重复查询的 SQL Server 表,我需要提高它的性能。我需要检索与此处显示的

WHERE
子句匹配的行。所有 3 列都定义为
datetime2
列。

WHERE GREATEST(dateColumn1, dateColumn2) > dateColumn3

为表建立索引以提高性能的最佳方法是什么?

我应该创建一个计算列并为其建立索引吗?

sql sql-server indexing
2个回答
1
投票

为表建立索引以提高性能的最佳方法是什么?

将您的

WHERE
谓词子句表达式放入计算列中,然后对该列建立索引。


上次我检查时,SQL Server(包括 Azure SQL)不支持在

CREATE INDEX
语句中使用任意表达式,因此您想要索引的任何表达式都必须定义为计算(但不一定是
PERSISTED
)列
.

...几个月前我自己就这样做了,但我遇到了一个😩诱发的错误:如果你在SQL Server 2022 或 Azure SQL 中的计算列表达式。

现在,因为您需要定义一个将按原样索引的计算列,然后考虑您可以...
将计算列简单地定义为 
PERSISTED

的结果 - 这意味着您仍然需要在
LEAST

查询中进行 GREATEST 比较,并且即使发生最轻微的变化,也可能会遇到糟糕的查询计划选择你的数据库。

  1. ...或:使用

    GREATEST( col1, col2, etc )
    表达式的结果定义
    另一个
    计算列,并在其上定义
    >
    ;您需要更新您的

    SELECT
  2. 查询以直接引用新的计算列,但这会显着降低 SQL Server 生成错误查询计划的几率(但 YMMV、ofc)。
  3. (我假设你的

    >
    INDEX
    列都是
    SELECT
    。(如果它们实际上是

    dateCol1
  4. ,那么这个答案可能对你不起作用,因为这里的计算列和索引不会处理
dateCol2

datetime2(7) NOT NULL
 式谓词)
无论如何,像这样:
NULL
请注意,我定义了 dateCol1 IS NOT NULL
dateCol1 IS NOT DISTINCT FROM dateCol2
- 以及关联的

-- Before doing anything, ensure we're in a deterministic and standards-compliant environment, which is important for PERSISTED columns: SET ANSI_NULLS, ANSI_PADDING, ANSI_WARNINGS, ARITHABORT, CONCAT_NULL_YIELDS_NULL, QUOTED_IDENTIFIER ON SET XACT_ABORT ON; /* See https://stackoverflow.com/questions/1150032/what-is-the-benefit-of-using-set-xact-abort-on-in-a-stored-procedure */ BEGIN TRANSACTION ddlTxn; RAISERROR( 'Now running ALTER TABLE...', 0, 1) WITH NOWAIT; ALTER TABLE dbo.MyTable ADD Greatest_1_or_2 AS GREATEST( dateCol1, dateCol2 ) PERSISTED NOT NULL, DateCol_1_or_2_is_gt_3 AS ( CASE WHEN GREATEST( dateCol1, dateCol2 ) > dateCol3 THEN CONVERT(bit,1) ELSE CONVERT(bit,0) END ) PERSISTED NOT NULL, DateCol_1_or_2_diff_3 AS ( DATEDIFF( microsecond, GREATEST( dateCol1, dateCol2 ) - dateCol3 ) PERSISTED NOT NULL; GO /* This `GO` is to stop SSMS complaining that the computed cols ( defined above, but used below) won't exist at parse-time. */ RAISERROR( 'Now running CREATE INDEX...', 0, 1) WITH NOWAIT; CREATE INDEX IX_Greatest12 ON dbo.MyTable ( Greatest_1_or_2 ) INCLUDE ( dateCol3 ); CREATE INDEX IX_Greatest12IsGT3 ON dbo.MyTable ( DateCol_1_or_2_is_gt_3 ); CREATE INDEX IX_DateCol12Diff3 ON dbo.MyTable ( DateCol_1_or_2_diff_3 ); RAISERROR( 'COMMITing TRANSACTION...', 0, 1) WITH NOWAIT; COMMIT TRANSACTION ddlTxn; RAISERROR( 'Ding!', 0, 1) WITH NOWAIT;

索引 - 因此您可能希望根据实际查询选择一组或另一组(或两者?)。

因此您的查询现在可能如下所示:

DateCol_1_or_2_is_gt_3

或:
DateCol_1_or_2_diff_3
或:

IX_

您需要自己测试和基准测试这 3 种不同的变体,因为总体上哪个更快或更优完全取决于数据库的其余部分如何工作,从我的角度来看,

在互联网上

,我什么也看不见:)
    

我看到几个选项:
您可以在 
SELECT /* ... */ FROM dbo.MyTable AS t WHERE t.Greatest_1_or_2 > t.dateCol3

上创建索引。 SQL Server 可以执行索引扫描并评估每行的条件,而无需执行成本更高的表扫描。这假设表格的“宽度”(所有列的组合)远大于三个日期列。 注意:

在测试时,我无法诱使 SQL Server 在表扫描上实际使用此索引,除非我添加了显式索引提示。这可能只是测试数据不足,或者可能还有其他限制条件。

0
投票

我认为,与基础索引数据检索的成本相比,

SELECT /* ... */ FROM dbo.MyTable AS t WHERE t.DateCol_1_or_2_is_gt_3 = 1;

SELECT /* ... */ FROM dbo.MyTable AS t WHERE t.DateCol_1_or_2_diff_3 < 0;
之间的差异可以忽略不计,但如果选择的话,后者可能具有轻微的性能优势。 另一种选择是向表中添加计算列并对结果建立索引,例如: TableName(dateColumn1, dateColumn2, dateColumn3)

然后您可以将查询条件更改为

GREATEST(dateColumn1, dateColumn2) > dateColumn3

旁注:仅当 (1) SQL Server 认为计算列表达式“精确”或 (2) 计算列定义为

dateColumn1 > dateColumn3 OR dateColumn2 > dateColumn3

时,才可以在计算列上定义索引。由于
ALTER TABLE Data
    ADD isInteresting AS
        CASE WHEN dateColumn1 > dateColumn3 OR dateColumn2 > dateColumn3
        THEN CAST(1 AS BIT) ELSE CAST(0 AS BIT) END
        PERSISTED

CREATE INDEX IX_Data_isInteresting ON Data(isInteresting)
函数不被 SQL Server 视为“精确”函数(可能是因为它支持浮点值),因此它的使用需要持久计算列。

WHERE isInteresting = 1

 替代方案
不需要

需要保留。

请参阅 
this db<>fiddle
 以获取显示执行计划的演示。
	
© www.soinside.com 2019 - 2024. All rights reserved.