第二个计算标量的解释

Mic*_*een 1 sql-server optimization sql-server-2019

我注意到一个奇怪的事情。这个查询

declare @t int = 3;

select distinct top(@t) 
    Number - Number + ABS(CHECKSUM( CAST(NEWID() as binary(16)) ))
from Numbers as n;
Run Code Online (Sandbox Code Playgroud)

制定这个计划

在此输入图像描述

dbo.Numbers 是一个标准的 Numbers 表 - 只是具有聚集主键的连续整数列表 - 包含 100k 行。

有两个计算标量运算符。右边的计算Number - Number并定义一个内部表达式。左边的执行加法和内部函数调用。奇怪的是,如果重写为,则只有一个计算标量运算符

Number + ABS(...) - Number
Run Code Online (Sandbox Code Playgroud)

这里发生了一些“有趣”的事情吗?或者这是解析器如何从备忘录构建执行计划的简单产物?两种形式都在 search(1) 中完成编译。使用 TF8677 强制搜索(2) 不会改变行为。TF8606 显示项目标准化后树中的分离。

我不认为这是一个问题。我只是想知道这里是否有值得学习的东西。

一些背景

我想生成一个固定长度的随机数列表。这

ABS(CHECKSUM(NEWID()))
Run Code Online (Sandbox Code Playgroud)

似乎是要走的路。我有一个 Numbers 表(有 100k 行),因此使用它来驱动输出似乎是合理的。我想要不进行替换的采样,因此 DISTINCT 将确保没有重复项,并且 TOP() 将给出所需的结果集大小。把它放在一起我得到

declare @t int = 3;

select distinct top(@t) 
    ABS(CHECKSUM(NEWID()))
from Numbers as n;
Run Code Online (Sandbox Code Playgroud)

遗憾的是,该计划是使用哈希匹配(聚合)进行聚集索引扫描。最好只读取所需的最小行数,也许可以使用 Flow Distinct 来强制唯一性。

这很快让我想到了这个问题。我尝试了那里的每一个建议。除了将基表列转换为字符串、附加字符串函数的空输出并重新转换为整数(我没有尝试 CCI)之外,没有任何更改计划。这感觉不太令人满意。

认识到 CHECKSUM 返回了整数,并且 Numbers 表保存了整数,我尝试了以下无操作:

declare @t int = 3;

select distinct top(@t) 
    Number - Number + ABS(CHECKSUM( CAST(NEWID() as binary(16)) ))
from Numbers as n;
Run Code Online (Sandbox Code Playgroud)

这给出了上面所希望的计划。

对于从 1 到 100k 的所有参数值,形状都会保持不变。

为了我跑步的价值

Microsoft SQL Server 2019 (RTM) - 15.0.2000.5 (X64) 2019 年 9 月 24 日 13:48:23 版权所有 (C) 2019 Microsoft Corporation Developer Edition(64 位),适用于 Windows 10 Home 10.0(内部版本 18362:)

英特尔酷睿 i7-8550U @ 2GHz,16GB RAM,SSD

Pau*_*ite 5

计算标量的数量没有什么特别有趣的。编译和优化过程通常会产生比严格需要的更多的内容。Conor Cunningham 在一次早期的 SQLBits 会议上笼统地谈到了这一点。在理想的情况下,不必要的标量计算将在优化结束时崩溃,但只实现了少数情况。

背景:Flow Distinct的选择取决于所需的行数是否小于估计的不同值的数量。

使用变量 withTOP意味着默认猜测需要 100 行,因此我们需要不同值的数量超过这个数量。

基数估计器 (CE) 的处理方式ABS(CHECKSUM(NEWID()))取决于版本。SQL Server 2014 中引入的 CE 模型估计该表达式将生成一个唯一值,无论其计算次数(在本例中为读取的 Numbers 表行数)如何。当然,这不是一个很好的估计。

2014 年之前的 CE 模型做出了更有根据的猜测,给出的估计值远远超过 100,因此具有Flow Distinct。不过,我的 Numbers 表确实有一千万行。@t如有必要,您可以通过暗示例如的较低值来帮助方程式顺利进行OPTIMIZE FOR (@t = 1)

您可以通过任何常用方式暗示早期的 CE 模型,例如USE HINT ('FORCE_LEGACY_CARDINALITY_ESTIMATION')USE HINT ('QUERY_OPTIMIZER_COMPATIBILITY_LEVEL_110')

例如:

DECLARE @T integer = 3;

SELECT DISTINCT TOP (@T)
    ABS(CHECKSUM(NEWID()))
FROM dbo.Numbers AS N
OPTION
(
    OPTIMIZE FOR (@T = 1),
    USE HINT('QUERY_OPTIMIZER_COMPATIBILITY_LEVEL_110')
);
Run Code Online (Sandbox Code Playgroud)

计划探索者计划