为什么在BIGINT col上进行此搜索具有额外的常量扫描,计算标量和嵌套循环运算符?


8

当我查看某些查询的实际执行计划时,我注意到WHERE子句中使用的文字常量显示为计算标量常量扫描的嵌套链。

sql studio屏幕截图

要重现此,我使用下表

CREATE TABLE Table1 (
    [col1] [bigint] NOT NULL,
    [col2] [varchar](50) NULL,
    [col3] [char](200) NULL
)
CREATE NONCLUSTERED INDEX IX_Table1 ON Table1 (col1 ASC)

其中包含一些数据:

INSERT INTO Table1(col1) VALUES (1),(2),(3),
                               (-9223372036854775808),
                               (9223372036854775807),
                               (2147483647),(-2147483648)

当我运行以下(废话)查询时:

SELECT a.col1, a.col2
  FROM Table1 a, Table1 b
  WHERE b.col1 > 2147483648

我看到它将在Index Seek和标量计算(根据常量)的结果中进行嵌套循环绘制。

请注意,文字大于maxint。它确实有助于写作CAST(2147483648 as BIGINT)。您知道为什么MSSQL会在执行计划中使用它吗?有没有比使用强制转换更短的方法来避免它了?它是否也会影响到预准备语句的绑定参数(来自jtds JDBC)?

标量计算并不总是完成的(似乎是特定于索引查找的)。有时查询分析器不会以图形方式显示它,而是像col1 < scalar(expr1000)谓词属性中那样。

我已经在Windows 7上使用MS SSMS 2016(13.0.16100.1)和SQL Server 2014 Expres Edition 64bit看到了这一点,但我想这是一个普遍的行为。

Answers:


8
SELECT thing, 
       sql_variant_property(thing,'basetype') AS basetype,
       sql_variant_property(thing,'precision') AS precision, 
       sql_variant_property(thing,'scale') AS scale
FROM (VALUES (2147483648)) V(thing)

向您显示文字2147483648被解释为numeric(10,0)。此行为早bigint于SQL Server(2000)中的引入。

没有语法表明应将文字视为bigint-添加显式CAST是最佳解决方案。“ 动态查找和隐藏的隐式转换”一文讨论了计划中其余的设备。

该计划本身表明,嵌套循环在以下位置具有搜索谓词

Seek Keys[1]: Start: [tempdb].[dbo].[Table1].col1 > Scalar Operator([Expr1005]), 
                End: [tempdb].[dbo].[Table1].col1 < Scalar Operator([Expr1006])

您可以使用扩展事件会话query_trace_column_values来查看以下内容。

在此处输入图片说明

计划中的XML也显示了这一点

  <DefinedValue>
    <ValueVector>
      <ColumnReference Column="Expr1005" />
      <ColumnReference Column="Expr1006" />
      <ColumnReference Column="Expr1004" />
    </ValueVector>
    <ScalarOperator ScalarString="GetRangeWithMismatchedTypes((2147483648.),NULL,(6))">
      <Intrinsic FunctionName="GetRangeWithMismatchedTypes">
        <ScalarOperator>
          <Const ConstValue="(2147483648.)" />
        </ScalarOperator>
        <ScalarOperator>
          <Const ConstValue="NULL" />
        </ScalarOperator>
        <ScalarOperator>
          <Const ConstValue="(6)" />
        </ScalarOperator>
      </Intrinsic>
    </ScalarOperator>
  </DefinedValue>

这并不意味着它实际上是在进行比较,< null而是

范围边界表达式使用NULL表示两端的“无界”。来源

因此,最终结果是您的查询谓词b.col1 > CAST(2147483648 AS NUMERIC(10, 0))仍然以b.col1 > CAST(2147483648 AS BIGINT)

它是否也会影响到预准备语句的绑定参数(来自jtds JDBC)?

我没有使用过jtds JDBC,但我想它允许您定义参数数据类型?如果是这样,只需确保参数是与列(bigint)匹配的正确数据类型即可,因此SQL Server无需处理不匹配的数据类型。


3

关于我有关JDBC准备语句的问题。jTDS使用sp_prepare/ sp_execute(默认prepareSQL=3模式)。

使用以下查询():

use database
select
    cp.objtype, st.text
from sys.dm_exec_cached_plans cp
cross apply sys.dm_exec_sql_text(cp.plan_handle) st
where cp.objtype = 'prepared' and st.text like '%TABLE%'

我可以看到JTDS发出的准备好的语句,它确实(@P0 bigint)...按预期声明了变量。

因此,这一切都很好,我需要记住,在尝试执行计划时,最好是实际定义局部类型的变量,而不是用文字替换它们(和/或sp_execute用于获取缓存的执行计划)。


1
或者,您可以始终将自己的文字转换为要匹配的列的类型,这是一条规则。
Andriy M,
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.