これがBIGINT colでシークするのに、追加の定数スキャン、計算スカラー、ネストされたループ演算子があるのはなぜですか?


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

インデックスシークとスカラー計算(定数から)の結果で、ネストされたループの描画を行うことがわかります。

リテラルが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 64ビットでこれを見たことがありますが、これは一般的な動作だと思います。

回答:


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準備済みステートメントに関する私の質問に関連して。jTDSsp_prepare/を使用sp_executeします(デフォルトprepareSQL=3モード)。

次のクエリ(source)の場合:

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
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.