Geo*_*son 26 sql-server varchar functions sql-server-2014 cardinality-estimates
我有一个带有字符串列的表和一个检查具有特定长度的行的谓词。在 SQL Server 2014 中,无论我检查的长度如何,我都会看到估计为 1 行。这产生了非常糟糕的计划,因为实际上有数千甚至数百万行,而 SQL Server 选择将此表放在嵌套循环的外侧。
对于 SQL Server 2014 的基数估计为 1.0003 而 SQL Server 2012 估计为 31,622 行,是否有解释?有没有好的解决方法?
以下是该问题的简短再现:
-- Create a table with 1MM rows of dummy data
CREATE TABLE #customers (cust_nbr VARCHAR(10) NOT NULL)
GO
INSERT INTO #customers WITH (TABLOCK) (cust_nbr)
SELECT TOP 1000000
CONVERT(VARCHAR(10),
ROW_NUMBER() OVER (ORDER BY (SELECT NULL))) AS cust_nbr
FROM master..spt_values v1
CROSS JOIN master..spt_values v2
GO
-- Looking for string of a certain length.
-- While both CEs yield fairly poor estimates, the 2012 CE is much
-- more conservative (higher estimate) and therefore much more likely
-- to yield an okay plan rather than a drastically understimated loop join.
-- 2012: 31,622 rows estimated, 900K rows actual
-- 2014: 1 row estimated, 900K rows actual
SELECT COUNT(*)
FROM #customers
WHERE LEN(cust_nbr) = 6
OPTION (QUERYTRACEON 9481) -- Optionally, use 2012 CE
GO
Run Code Online (Sandbox Code Playgroud)
我还阅读了SQL Server 2014 Cardinality Estimator 上的白皮书,但没有找到任何说明情况的内容。
Zan*_*ane 20
对于旧版 CE,我看到估计值是 3.16228% 的行——这是用于列 = 字面谓词的“幻数”启发式(还有其他基于谓词构造的启发式方法——但LEN环绕列的旧版 CE 结果与此猜测框架匹配)。您可以在Joe Sack撰写的关于Selectivity Guesses in without Statistics和Ian Jose撰写的Constant-Constant比较估计的帖子中看到此类示例。
-- Legacy CE: 31622.8 rows
SELECT COUNT(*)
FROM #customers
WHERE LEN(cust_nbr) = 6
OPTION ( QUERYTRACEON 9481); -- Legacy CE
GO
Run Code Online (Sandbox Code Playgroud)
现在对于新的 CE 行为,看起来优化器现在可以看到它(这意味着我们可以使用统计信息)。我通过查看下面的计算器输出进行了练习,您可以将相关的自动生成统计信息视为指针:
-- New CE: 1.00007 rows
SELECT COUNT(*)
FROM #customers
WHERE LEN(cust_nbr) = 6
OPTION ( QUERYTRACEON 2312 ); -- New CE
GO
-- View New CE behavior with 2363 (for supported option use XEvents)
SELECT COUNT(*)
FROM #customers
WHERE LEN(cust_nbr) = 6
OPTION (QUERYTRACEON 2312, QUERYTRACEON 2363, QUERYTRACEON 3604, RECOMPILE); -- New CE
GO
/*
Loaded histogram for column QCOL:
[tempdb].[dbo].[#customers].cust_nbr from stats with id 2
Using ambient cardinality 1e+006 to combine distinct counts:
999927
Combined distinct count: 999927
Selectivity: 1.00007e-006
Stats collection generated:
CStCollFilter(ID=2, CARD=1.00007)
CStCollBaseTable(ID=1, CARD=1e+006 TBL: #customers)
End selectivity computation
*/
EXEC tempdb..sp_helpstats '#customers';
--Check out AVG_RANGE_ROWS values (for example - plenty of ~ 1)
DBCC SHOW_STATISTICS('tempdb..#customers', '_WA_Sys_00000001_B0368087');
--That's my Stats name yours is subject to change
Run Code Online (Sandbox Code Playgroud)
不幸的是,该逻辑依赖于对不同值数量的估计,而不会针对LEN函数的效果进行调整。
您可以通过将 重写LEN为 a来获得两种 CE 模型下基于 trie 的估计LIKE:
SELECT COUNT_BIG(*)
FROM #customers AS C
WHERE C.cust_nbr LIKE REPLICATE('_', 6);
Run Code Online (Sandbox Code Playgroud)
有关使用的跟踪标志的信息:
Sol*_*zky 13
对于 SQL 2014 的基数估计为 1.0003 而 SQL 2012 估计为 31,622 行,是否有解释?
我认为@Zane 的回答很好地涵盖了这一部分。
有没有好的解决方法?
您可以尝试为该计算列创建一个非持久化计算列,LEN(cust_nbr)并(可选)在该计算列上创建一个非聚集索引。这应该为您提供准确的统计数据。
我做了一些测试,这是我发现的:
PERSISTED(无索引)比其他两个变体更好。估计行数更准确。CPU 和运行时间更好(正如预期的那样,因为它不必每行计算任何内容)。PERSISTED:-(| 归档时间: |
|
| 查看次数: |
1486 次 |
| 最近记录: |