我有一个带有重复查询的 SQL Server 表,我需要提高它的性能。我需要检索与此处显示的
WHERE
子句匹配的行。所有 3 列都定义为 datetime2
列。
WHERE GREATEST(dateColumn1, dateColumn2) > dateColumn3
为表建立索引以提高性能的最佳方法是什么?
我应该创建一个计算列并为其建立索引吗?
为表建立索引以提高性能的最佳方法是什么?
将您的
WHERE
谓词子句表达式放入计算列中,然后对该列建立索引。
上次我检查时,SQL Server(包括 Azure SQL)不支持在
CREATE INDEX
语句中使用任意表达式,因此您想要索引的任何表达式都必须定义为计算(但不一定是 PERSISTED
)列 .
...几个月前我自己就这样做了,但我遇到了一个😩诱发的错误:如果你在SQL Server 2022 或 Azure SQL 中的计算列表达式。
现在,因为您需要定义一个将按原样索引的计算列,然后考虑您可以...
将计算列简单地定义为
PERSISTED
的结果 - 这意味着您仍然需要在 LEAST
查询中进行 GREATEST
比较,并且即使发生最轻微的变化,也可能会遇到糟糕的查询计划选择你的数据库。
...或:使用
GREATEST( col1, col2, etc )
表达式的结果定义 另一个计算列,并在其上定义
>
;您需要更新您的 SELECT
(我假设你的
>
和INDEX
列都是SELECT
。(如果它们实际上是dateCol1
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_
在互联网上
,我什么也看不见:)我看到几个选项:您可以在
SELECT /* ... */ FROM dbo.MyTable AS t WHERE t.Greatest_1_or_2 > t.dateCol3
上创建索引。 SQL Server 可以执行索引扫描并评估每行的条件,而无需执行成本更高的表扫描。这假设表格的“宽度”(所有列的组合)远大于三个日期列。 注意:
在测试时,我无法诱使 SQL Server 在表扫描上实际使用此索引,除非我添加了显式索引提示。这可能只是测试数据不足,或者可能还有其他限制条件。我认为,与基础索引数据检索的成本相比,
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
以获取显示执行计划的演示。