LIKE运算符的基数估计(局部变量)


24

我给人的印象是,LIKE在对未知场景进行所有优化时都使用运算符时,旧的和​​新的CE都使用9%的估计值(假设相关统计数据可用并且查询优化器不必求助于选择性猜测)。

当对信用数据库执行以下查询时,在不同的CE下我得到了不同的估计。在新的CE下,我收到了900行的估计值,而在旧的CE下,我收到了241.416的估计值,我不知道如何得出此估计值。有人能说清楚吗?

-- New CE (Estimate = 900)
DECLARE @LastName VARCHAR(15) = 'BA%'
SELECT * FROM [Credit].[dbo].[member]
WHERE [lastname] LIKE @LastName;

-- Forcing Legacy CE (Estimate = 241.416)
DECLARE @LastName VARCHAR(15) = 'BA%'
SELECT * FROM [Credit].[dbo].[member]
WHERE [lastname] LIKE @LastName
OPTION (
QUERYTRACEON 9481,
QUERYTRACEON 9292,
QUERYTRACEON 9204,
QUERYTRACEON 3604
);

在我的场景中,我已经将信用数据库设置为兼容性级别120,因此为什么在第二个查询中我使用跟踪标志来强制使用旧版CE,并提供有关查询优化器使用/考虑的统计信息的信息。我可以看到正在使用有关“姓氏”的列统计信息,但是我仍然无法弄清楚如何得出241.416的估算值。

除了Itzik Ben-Gan的这篇文章外,我找不到任何在线内容,该文章指出:“在针对未知场景进行所有优化时使用LIKE谓词时,传统CE和新CE均使用9%的估计值。” 该帖子中的信息似乎不正确。

Answers:


28

LIKE 您的情况下的猜测基于:

  • G:标准的9%猜测(sqllang!x_Selectivity_Like
  • M:6的倍数(幻数)
  • D:平均数据长度(以字节为单位)(来自统计信息),四舍五入为整数

具体来说,sqllang!CCardUtilSQL7::ProbLikeGuess用途:

Selectivity (S) = G / M * LOG(D)

笔记:

  • LOG(D)如果D介于1和2之间,则省略该术语。
  • 如果D小于1(包括缺失或NULL统计信息):
    D = FLOOR(0.5 * maximum column byte length)

这种怪异和复杂性是原始CE所特有的。

在问题示例中,平均长度为5(DBCC SHOW_STATISTICS四舍五入为5.6154 ):

估计= 10,000 *(0.09 / 6 * LOG(5))= 241.416

其他示例值:

 D   = 使用公式计算S
 15 = 406.208
 14 = 395.859
 13 = 384.742
 12 = 372.736
 11 = 359.684
 10 = 345.388
 09 = 329.584
 08 = 311.916
 07 = 291.887
 06 = 268.764
 05 = 241.416
 04 = 207.944
 03 = 164.792
 02 = 150.000(未使用日志)
 01 = 150.000(未使用日志)
 00 = 291.887(LOG 7)/ * FLOOR(0.5 * 15)[15,因为姓氏是varchar(15)] * /

试验台

DECLARE
    @CharLength integer = 5, -- Set length here
    @Counter integer = 1;

CREATE TABLE #T (c1 varchar(15) NULL);

-- Add 10,000 rows
SET NOCOUNT ON;
SET STATISTICS XML OFF;

BEGIN TRANSACTION;
WHILE @Counter <= 10000
BEGIN
    INSERT #T (c1) VALUES (REPLICATE('X', @CharLength));
    SET @Counter = @Counter + 1;
END;
COMMIT TRANSACTION;

SET NOCOUNT OFF;
SET STATISTICS XML ON;

-- Test query
DECLARE @Like varchar(15);
SELECT * FROM #T AS T 
WHERE T.c1 LIKE @Like;

DROP TABLE #T;

15

我在使用旧版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,它与您在问题中的匹配。如果我在表中未使用某个值,则会出现一些错误。

这里的模型并不完美,但我认为它们很好地说明了一般行为。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.