问题: 我可以以某种方式提示SQL Server从索引查找返回的预期行数吗?
背景:
我有一个唯一的聚集索引:
ALTER TABLE [dbo].[T] ADD CONSTRAINT [X] PRIMARY KEY CLUSTERED
(
[Int1] ASC,
[Int2] ASC,
[Int3] ASC,
[Int4] ASC
)
现在,我有一个查询可获取特定的单个值:
SELECT
...
FROM [dbo].[T]
WHERE
[Int1] = @Int1 AND
[Int2] = @Int2 AND
[Int3] = @Int3 AND
[Int4] = @Int4
这立即运行。具有参数@ Int1-4
的任何值现在我实际上想要一个值范围。 如果我以@ Int4的值递增的方式进行循环进行迭代(是的-在SQL中听起来完全错误的话)-我会立即获得结果。
-- Looks completely wrong for SQL - but it seems to be fastest way to fetch range of values
DECLARE @I INT = 1
WHILE @I <= 50
BEGIN
SELECT
...
FROM [dbo].[T]
WHERE
[Int1] = @Int1 AND
[Int2] = @Int2 AND
[Int3] = @Int3 AND
[Int4] = @I
SET @I = @I + 1
END
GO
如果我将最后一个条件指定为范围:
SELECT
...
FROM [dbo].[T]
WHERE
[Int1] = @Int1 AND
[Int2] = @Int2 AND
[Int3] = @Int3 AND
[Int4] BETWEEN @Int4 AND (@Int4 + 2)
查询需要几分钟。 如果完全省略[Int4]约束,也会发生同样的情况。
在所有3种情况下,实际的执行计划看起来都是相同的(聚集索引查找):
差异在于返回的估计行与实际行。如果是精确条件,则两者均为1。如果是之间条件或省略条件,则相差很大:
为什么估算差异会严重损害性能? 有什么方法可以使之间或被忽略的条件更快地运行?有什么方法可以暗示SQL行数会很低吗?
顺便说一句。该表包含730亿行。数据大小约为1.7TB,索引大小为4.2TB。 它可能可以重建,但是将需要大量的停机时间。另外,如果我只是切换到虚拟周期,则可以使查询快速进行。
EDIT1:
根据要求-这是表和索引的实际DDL(在上面的简化示例中,前4列是INT1-INT4):
CREATE TABLE [dbo].[RelationalResultValueVectorial](
[RelationalResultRowId] [bigint] NOT NULL,
[RelationalResultPropertyId] [int] NOT NULL,
[RelationalResultVectorialDimensionId] [int] NOT NULL,
[OrdinalRowIdWithinProperty] [int] NOT NULL,
[RelationalResultValueId] [bigint] IDENTITY(1,1) NOT NULL,
CONSTRAINT [Idx_RelationalResultValueVectorial] PRIMARY KEY CLUSTERED
(
[RelationalResultRowId] ASC,
[RelationalResultPropertyId] ASC,
[RelationalResultVectorialDimensionId] ASC,
[OrdinalRowIdWithinProperty] ASC
) ON [RelationalDataFileGroup]
) ON [RelationalDataFileGroup]
GO
CREATE UNIQUE NONCLUSTERED INDEX [IX_RelationalResultValueVectorial_ValueId] ON [dbo].[RelationalResultValueVectorial]
(
[RelationalResultValueId] ASC
) ON [RelationalDataFileGroup]
GO
-- + some FKs
EDIT2:
关于参数嗅探的答案-这是我仅使用常量时得到的结果(估计仍然错误,执行速度仍然很慢):
答案 0 :(得分:0)
这是因为您使用了variables
,并且它们在查询中并未像上面所写的那样被“嗅探”。
如果组成primary key
的4个字段中的每个都有单个值,则不需要知道这些值,因为服务器知道这4个字段的每个组合都是唯一的。
使用条件[Int4] BETWEEN @Int4 AND (@Int4 + 2)
时情况有所不同。结果集的基数可能会有所不同,您可以指定1个值的范围或@int4
的所有可能值,如果您不要求服务器使用{{1 }},服务器将估计optin(recompile)
的基数为行的9%(从between
开始为16%)。
尝试用sql server 2014
替换变量,基数估计将基于constants
,现在它估计“未知值”。
因此,您的案例的解决方案是使用statistics
选项。