Als*_*sin 7 sql-server partitioning cardinality-estimates
我想知道为什么 SQL Server 在如此简单的情况下会做出错误的估计。有一个场景。
CREATE PARTITION FUNCTION PF_Test (int) AS RANGE RIGHT
FOR VALUES (20140801, 20140802, 20140803)
CREATE PARTITION SCHEME PS_Test AS PARTITION PF_Test ALL TO ([Primary])
CREATE TABLE A
(
DateKey int not null,
Type int not null,
constraint PK_A primary key (DateKey, Type) on PS_Test(DateKey)
)
INSERT INTO A (DateKey, Type)
SELECT
DateKey = N1.n + 20140801,
Type = N2.n + 1
FROM dbo.Numbers N1
cross join dbo.Numbers N2
WHERE N1.n BETWEEN 0 AND 2
and N2.n BETWEEN 0 AND 10000 - 1
UPDATE STATISTICS A (PK_A) WITH FULLSCAN, INCREMENTAL = ON
CREATE TABLE B
(
DateKey int not null,
SubType int not null,
Type int not null,
constraint PK_B primary key (DateKey, SubType) on PS_Test(DateKey)
)
INSERT INTO B (DateKey, SubType, Type)
SELECT
DateKey,
SubType = Type * 10000 + N.n,
Type
FROM A
cross join dbo.Numbers N
WHERE N.n BETWEEN 1 AND 10
UPDATE STATISTICS B (PK_B) WITH FULLSCAN, INCREMENTAL = ON
Run Code Online (Sandbox Code Playgroud)
所以设置非常简单,统计信息就位,当我们查询一张表时,SQL Server 可以产生正确的估计。
select COUNT(*) from A where DateKey = 20140802
--10000
select COUNT(*) from B where DateKey = 20140802
--100000
Run Code Online (Sandbox Code Playgroud)
但是在这个简单的选择中,估计是很遥远的,我看不出为什么。
SELECT a.DateKey, a.Type
FROM A
JOIN B
ON b.DateKey = a.DateKey
AND b.Type = a.Type
WHERE a.DateKey = 20140802
Run Code Online (Sandbox Code Playgroud)
在聚集索引搜索之后,估计与实际相差 57%!现实世界的查询更糟,估计与实际相差 2%。
PS 数字表重现设置
DECLARE @UpperBound INT = 1000000;
;WITH cteN(Number) AS
(
SELECT ROW_NUMBER() OVER (ORDER BY s1.[object_id]) - 1
FROM sys.all_columns AS s1
CROSS JOIN sys.all_columns AS s2
)
SELECT n = [Number] INTO dbo.Numbers
FROM cteN WHERE [Number] <= @UpperBound;
CREATE UNIQUE CLUSTERED INDEX CIX_Number ON dbo.Numbers(n)
WITH
(
FILLFACTOR = 100, -- in the event server default has been changed
DATA_COMPRESSION = ROW -- if Enterprise & table large enough to matter
);
Run Code Online (Sandbox Code Playgroud)
PPS 相同的场景但非分区工作完美。
估计(使用新的基数估计器)适用于普通连接,但在优化器考虑colocated join选项时不太准确。
当连接以相同方式分区的两个表时,可以使用并置连接(又名按分区连接)。这个想法是一次加入一个分区,使用嵌套循环应用由恒定扫描(内存中的值表)提供的分区 ID 驱动。
由于并置连接涉及嵌套循环应用,您可以通过指定OPTION (HASH JOIN)例如强制优化器避免这种情况:
该计划中的两个目标是:
Seek Keys[1]: Prefix:
PtnId1000, [dbo].[A].DateKey = Scalar Operator((3)), Scalar Operator((20140802))
Seek Keys[1]: Prefix:
PtnId1003, [dbo].[B].DateKey = Scalar Operator((3)), Scalar Operator((20140802))
Run Code Online (Sandbox Code Playgroud)
优化器在这两种情况下都应用了静态分区消除,为两种搜索和以下连接提供了准确的估计。
当优化器考虑并置连接(如问题所示)时,搜索是:
Seek Keys[1]: Prefix:
PtnId1000, [dbo].[A].DateKey = Scalar Operator([Expr1006]), Scalar Operator((20140802))
Seek Keys[1]: Prefix:
PtnId1003, [dbo].[B].DateKey = Scalar Operator([Expr1006]), Scalar Operator((20140802))
Run Code Online (Sandbox Code Playgroud)
...其中[Expr1006]是 Constant Scan 运算符返回的值。
基数估计器现在看不到DateKey值和分区 id 是相互依赖的,就像使用文字常量时一样。换句话说,对于估计器来说,内部的值[Expr1006]指定与 相同的分区并不明显DateKey = 20140802。
因此,CE 选择(默认情况下)使用正常指数退避方法来估计两个(显然独立的)谓词的选择性。
这解释了馈送连接的减少的基数估计。此选项的较低明显成本(由于错误估计)意味着优化器选择并置连接而不是常规连接,即使很明显(对人类而言)它没有提供任何价值。
有几种方法可以解决逻辑中的这个差距,包括使用查询提示USE HINT ('ASSUME_MIN_SELECTIVITY_FOR_FILTER_ESTIMATES'),但这将影响整个查询,而不仅仅是有问题的并置连接替代方案。正如 Erik 在他的回答中指出的那样,您还可以暗示使用旧版 CE。
有关并置连接的更多信息,请参阅我的文章提高分区表连接性能
这似乎是由于 SQL Server 2014 中引入的新基数估计器所致。
如果您指示查询使用旧的,您会得到不同的计划和正确的估计。
SELECT a.DateKey, a.Type
FROM A AS a
JOIN B AS b
ON b.DateKey = a.DateKey
AND b.Type = a.Type
WHERE a.DateKey = 20140802
OPTION(USE HINT('FORCE_LEGACY_CARDINALITY_ESTIMATION'));
Run Code Online (Sandbox Code Playgroud)
有关更多信息,请参阅这些链接:
| 归档时间: |
|
| 查看次数: |
391 次 |
| 最近记录: |