Почему при поиске в столбце BIGINT используются операторы дополнительного постоянного сканирования, вычисления скалярных и вложенных циклов?

8

Когда я смотрю на фактический план выполнения некоторых из моих запросов, я замечаю, что литеральные константы, используемые в предложении WHERE, отображаются в виде вложенной цепочки вычисления скаляра и константы сканирования .

скриншот студии sql

Чтобы воспроизвести это, я использую следующую таблицу

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

Я вижу, что он выполнит рисование Nested Loop в результате поиска по индексу и скалярного вычисления (из константы).

Обратите внимание, что литерал больше, чем maxint. Это помогает писать CAST(2147483648 as BIGINT). Любая идея, почему MSSQL откладывает это на план выполнения и есть более короткий способ избежать этого, чем использование приведения? Влияет ли это на параметры привязки к подготовленным операторам (из jtds JDBC)?

Скалярное вычисление не всегда выполняется (кажется, что поиск по индексу специфичен). И иногда анализатор запросов показывает его не графически, а col1 < scalar(expr1000)в свойствах предиката.

Я видел это с MS SSMS 2016 (13.0.16100.1) и SQL Server 2014 Expres Edition 64bit на Windows 7, но я предполагаю, что это общее поведение.

Экес
источник

Ответы:

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, но я предполагаю, что он позволяет вам определять типы данных параметров? Если это так, просто убедитесь, что параметры имеют правильный тип данных, который соответствует column ( bigint), поэтому SQL Server не нужно иметь дело с несовпадающими типами данных.

Мартин Смит
источник
3

По отношению к моему вопросу о JDBC подготовлены заявления. jTDS использует sp_prepare/ sp_executeprepareSQL=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
В качестве альтернативы вы можете установить для себя правило всегда приводить литералы к типу столбца, с которым вы сопоставляете.
Андрей М