筛选索引上的最大键长度警告,但由于筛选器,实际键总是更小 - 这安全吗?

Maximum key length warning on a filtered index, but the actual key will always be smaller because of the filter - is this safe?

提问人:SoItBegins 提问时间:10/28/2023 最后编辑:SoItBegins 更新时间:10/28/2023 访问量:48

问:

我正在优化 SQL Server 2019 中的查询。这是一个非常大的表的数据查找,涉及返回大量数据,因此我正在为它创建一个自定义索引。

以下是索引:

CREATE UNIQUE NONCLUSTERED INDEX UQ_DataValues_FilteredEndVSNullDesc2
ON [dbo].[DataValues] (
    [EndTime] ASC,
    [ValueString] ASC, 
    [DomainId] ASC,
    [FieldType] ASC,
    [StartTime] DESC
)
INCLUDE ([Id],[ValueNumber])
WHERE ([EndTime] IS NULL AND [ValueString] IS NULL);

数据库表中的每一行都必须具有 ValueNumber () 或 ValueString () 之一。另一个值将为 NULL。每行还必须有一个 StartTime (),并且可能有一个 EndTime(也是)。如果某行没有 EndTime,则它将为 NULL。floatnvarchar(4000)datetimeoffset(7)datetimeoffset(7)

对于此特定查询,我知道我正在查找的数据将设置 ValueNumber 和 ValueString NULL。我也知道它是一种没有 EndTime 的类型——因此是索引过滤器。

我遇到的问题是,当我创建索引时,MSSQL警告:

Warning! The maximum key length for a nonclustered index is 1700 bytes. The index 'UQ_DataValues_FilteredEndVSNull' has maximum length of 8028 bytes. For some combination of large values, the insert/update operation will fail.

好吧,我确实将 ValueString 放在索引键中。但:

  • 由于过滤器,我知道 ValueString 将始终为 NULL。
  • 如果我没有将 ValueString(或 EndTime)放在键中,则查询优化器将在我执行查找时对其进行聚簇索引扫描,即使它与索引的筛选器谓词完全匹配(即使我使用索引提示)。这效率要低得多。

我的问题是:这安全吗具有 ValueString 的数据将被添加到表中,但不会添加到索引中,因此此特定键公式实际上不会导致任何插入/更新失败 - 是吗?

编辑:我被要求包含示例查询。这是它大致的样子。实体框架将在实际查找中生成查询,但它是这样的:

SELECT TOP (5000000) 
    [Project1].[Id] AS [Id], 
    [Project1].[FieldType] AS [FieldType], 
    [Project1].[StartTime] AS [StartTime], 
    [Project1].[ValueNumber] AS [ValueNumber], 
    [Project1].[DomainId] AS [DomainId]
    FROM ( SELECT 
        [Extent1].[Id] AS [Id], 
        [Extent1].[DomainId] AS [DomainId], 
        [Extent1].[FieldType] AS [FieldType], 
        [Extent1].[StartTime] AS [StartTime], 
        [Extent1].[ValueNumber] AS [ValueNumber]
        FROM [dbo].[DataValues] AS [Extent1] --WITH (INDEX(UQ_DataValues_FilteredEndVSNullDesc2)) , ideally
        WHERE ([Extent1].[EndTime] IS NULL) AND 
          ([Extent1].[ValueString] IS NULL) AND 
          ([Extent1].[StartTime] >= '01/01/2023') AND 
          ([Extent1].[StartTime] <= '10/26/2023') AND 
          ([Extent1].[DomainId] IN (1000003592, 1000003594, 1000003596, 1000003595, 1000004919, 1000004922, 1000004920, 1000004921, 1000003565, 1000003564, 1000003563, 1000003562, 1000004876, 1000004877, 1000004878, 1000004879, 1000003517, 1000003516, 1000003519, 1000003518, 1000003822, 1000003823, 1000003824, 1000003825, 1000003962, 1000003968, 1000003967, 1000003970)) AND 
          ([Extent1].[FieldType] IN (1))
    )  AS [Project1]
    ORDER BY [Project1].[Start] DESC

对于现有索引(不是上面显示的索引),此查询需要 1 分钟,该索引不包括 ValueNumber 或 ValueString。当我使用上面显示的索引时,在键中包括 ValueString 和 EndTime,时间下降到 50 秒。如果我不在键中包含 ValueString 和 EndTime,则与现有索引相比,我没有任何好处。

请注意,该查询是实际查询的玩具版本,使用现有索引需要 10 分钟 (!)。我想尽快得到它。

sql sql-server 实体框架 索引查询 优化

评论

0赞 Stu 10/28/2023
当值始终为 NULL 时,为什么要包括 EndTime 和 Valuestring?
0赞 SoItBegins 10/28/2023
因为如果我不这样做,查询优化器将对它们进行单独的聚簇索引扫描(如上所述)。为了让查询优化器依赖此索引(速度最快),我需要包含这两列。
1赞 Joel Coehoorn 10/28/2023
@SoItBegins 这听起来像是有问题的查询正在执行 SELECT *,而不是指定它需要的列。
1赞 Stu 10/28/2023
您能否包含实际查询。仅当优化程序能够保证过滤后的索引始终覆盖所有符合条件的行时,才会使用过滤索引。如果要筛选的列永远不能同时为空,那么显然筛选器子句不正确 - 您希望其中一个或另一个为 null。
0赞 SoItBegins 10/28/2023
好的,更新了它。

答: 暂无答案