我在使用旧版CE的SQL Server 2014上进行了测试,基数估计也未达到9%。我在网上找不到任何准确的东西,所以我做了一些测试,发现了一个模型,该模型适合我尝试过的所有测试用例,但是我不确定它是否完整。
在我发现的模型中,估计值是从表中的行数,已过滤列的统计信息的平均键长度以及有时是已过滤列的数据类型长度得出的。有两种不同的公式用于估算。
如果FLOOR(平均键长)= 0,则估计公式将忽略列统计信息,并根据数据类型长度创建一个估计。我仅使用VARCHAR(N)进行了测试,因此NVARCHAR(N)可能存在不同的公式。这是VARCHAR(N)的公式:
(行估计)=(表中的行)*(-0.004869 + 0.032649 * log10(数据类型的长度))
这非常合适,但是不够准确:
x轴是数据类型的长度,y轴是具有一百万行的表的估计行数。
如果您没有该列的统计信息,或者该列具有足够的NULL值以将平均键长度降至1以下,则查询优化器将使用此公式。
例如,假设您有一个具有15万行的表,并且对VARCHAR(50)进行了过滤,并且没有列统计信息。行估计预测为:
150000 *(-0.004869 + 0.032649 * log10(50))= 7590.1行
SQL对其进行测试:
CREATE TABLE X_CE_LIKE_TEST_1 (
STRING VARCHAR(50)
);
CREATE STATISTICS X_STAT_CE_LIKE_TEST_1 ON X_CE_LIKE_TEST_1 (STRING) WITH NORECOMPUTE;
WITH
L0 AS (SELECT 1 AS c UNION ALL SELECT 1),
L1 AS (SELECT 1 AS c FROM L0 A CROSS JOIN L0 B),
L2 AS (SELECT 1 AS c FROM L1 A CROSS JOIN L1 B),
L3 AS (SELECT 1 AS c FROM L2 A CROSS JOIN L2 B),
L4 AS (SELECT 1 AS c FROM L3 A CROSS JOIN L3 B CROSS JOIN L2 C),
NUMS AS (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS NUM FROM L4)
INSERT INTO X_CE_LIKE_TEST_1 WITH (TABLOCK) (STRING)
SELECT TOP (150000) 'ZZZZZ'
FROM NUMS
ORDER BY NUM;
DECLARE @LastName VARCHAR(15) = 'BA%'
SELECT * FROM X_CE_LIKE_TEST_1
WHERE STRING LIKE @LastName;
SQL Server给出的估计行计数为7242.47,这有点接近。
如果FLOOR(平均密钥长度)> = 1,则使用基于FLOOR(平均密钥长度)的值的不同公式。这是我尝试过的一些值的表格:
1 1.5%
2 1.5%
3 1.64792%
4 2.07944%
5 2.41416%
6 2.68744%
7 2.91887%
8 3.11916%
9 3.29584%
10 3.45388%
如果FLOOR(平均密钥长度)<6,则使用上表。否则,使用以下公式:
(行估计)=(表中的行)*(-0.003381 + 0.034539 * log10(FLOOR(平均密钥长度)))
这一款比另一款更好,但仍不够完美。
x轴是平均键长,y轴是具有一百万行的表的估计行数。
再举一个例子,假设您有一个表,该表包含1万行,平均键长为5.5,用于过滤列的统计信息。行估计为:
10000 * 0.241416 = 241.416行。
SQL对其进行测试:
CREATE TABLE X_CE_LIKE_TEST_2 (
STRING VARCHAR(50)
);
WITH
L0 AS (SELECT 1 AS c UNION ALL SELECT 1),
L1 AS (SELECT 1 AS c FROM L0 A CROSS JOIN L0 B),
L2 AS (SELECT 1 AS c FROM L1 A CROSS JOIN L1 B),
L3 AS (SELECT 1 AS c FROM L2 A CROSS JOIN L2 B),
L4 AS (SELECT 1 AS c FROM L3 A CROSS JOIN L3 B CROSS JOIN L2 C),
NUMS AS (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS NUM FROM L4)
INSERT INTO X_CE_LIKE_TEST_2 WITH (TABLOCK) (STRING)
SELECT TOP (10000)
CASE
WHEN NUM % 2 = 1 THEN REPLICATE('Z', 5)
ELSE REPLICATE('Z', 6)
END
FROM NUMS
ORDER BY NUM;
CREATE STATISTICS X_STAT_CE_LIKE_TEST_2 ON X_CE_LIKE_TEST_2 (STRING)
WITH NORECOMPUTE, FULLSCAN;
DECLARE @LastName VARCHAR(15) = 'BA%'
SELECT * FROM X_CE_LIKE_TEST_2
WHERE STRING LIKE @LastName;
行估计是241.416,它与您在问题中的匹配。如果我在表中未使用某个值,则会出现一些错误。
这里的模型并不完美,但我认为它们很好地说明了一般行为。