web-dev-qa-db-ja.com

この警告をトリガーするもの:式の型変換がクエリプランの選択の "CardinalityEstimate"に影響する場合がある

SQL Server 2016を使用して、このT-SQLステートメントの実行中にこの警告が表示される理由を理解しようとしています(テストを簡単にするためにテーブル変数を選択しました。これは、intタイプの列を持つDBテーブルで発生します上手):

_declare @test as table(
    col1 int
)

insert into @test (col1)
values(30500600)

select LEFT(test.col1, 2)
from @test as test
_

_30_は期待どおりに返されますが、実行プランを調べると、次のようなSELECTステートメントの警告が表示されます。

式の型変換(CONVERT_IMPLICIT(varchar(12)、[test]。[col1]、0))は、クエリプランの選択で「CardinalityEstimate」に影響する可能性があります

テーブルからプルせずに上記を同じように実行した場合:

_select LEFT(30500600, 2)
_

期待どおり_30_が返され、実行プランに警告はありません。

どちらのステートメントもintタイプに対してLEFT()を実行する必要があるため、一方が警告を返し、他方が警告を返さない理由がわかりません。何が起きてる?

編集:

_col1_データ型宣言をvarcharに変更してみましたが、エラーは解消されました。しかし、それでもintselect LEFT(30500600, 2)関数に直接渡した場合にLEFT()自体が警告を引き起こさない理由を説明していません。

元のクエリでcastからvarcharを試しましたが、同じ警告を返すLEFT(cast(test.col1 as varchar(12)), 2)です。

可能性のある重複

リンクされた重複は、計算された列がそのユーザーの問題であったと述べています。私の例では計算された列がないので、それは問題ではありません。これは、CONCAT関数でも同じであるLEFT関数を使用した文字列型への暗黙的な変換に関する回答の2番目の部分につながります。上記の編集の2番目の段落では、CASTを使用して明示的にvarcharに変換したので、変換が行われるとは思いませんか?ここに何か欠けているのは明らかです。

2
gbeaven

このクエリに「型変換/カーディナリティの見積もり」の警告がない理由:

SELECT LEFT(30500600, 2);

...基数の見積もりがないためです。これは、決定的な関数に渡される定数式です。「行」が1つだけ返されることが保証されています。警告は潜在的なカーディナリティーの問題(タイプ変換の単純な存在ではなく)に関するものなので、説明はそれと同じくらい簡単だと思います。

そのステートメントの推定実行プランを取得する場合、それは単一の「クエリなしの選択」要素です。

<?xml version="1.0" encoding="utf-16"?>
<ShowPlanXML xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" Version="1.518" Build="13.0.5081.1" xmlns="http://schemas.Microsoft.com/sqlserver/2004/07/showplan">
  <BatchSequence>
    <Batch>
      <Statements>
        <StmtSimple StatementCompId="1" StatementId="1" StatementText="select LEFT(30500600, 2)" StatementType="SELECT WITHOUT QUERY" RetrievedFromCache="false" />
      </Statements>
    </Batch>
  </BatchSequence>
</ShowPlanXML>

価値があるのは、テーブル変数のバージョンでさえ、SQL Server2017で警告が表示されないことです(変換が可能な場合、それは実際には無意味な警告であるためです) tは、カーディナリティーの見積もりに影響を与える可能性があります(例のように)。

明確にするために、その警告はalways無意味ではありません。この例を見てみましょう:

SELECT message_id 
INTO #SomeNumbers
from sys.messages;

SELECT * 
FROM #SomeNumbers
WHERE LEFT(message_id, 2) = '50';

これにより、一時テーブルに280,192行が挿入されます。 SELECTクエリにはこの警告があり、警告は正当です。 SQL Serverでは、実際に一致する行が2,156のみ(テーブルの1%未満)であるのに、28,019.2の一致する行(テーブルの10%)があると推定しています。

この入力をLEFT関数にキャストする例については、警告がCONVERT_IMPLICITからCONVERTにわずかに変更されていることに注意してください。

式の型変換(CONVERT(varchar(12)、[test]。[col1]、0))は、クエリプランの選択で "CardinalityEstimate"に影響する可能性があります

つまり、暗黙の変換を明示的な変換に置き換えるだけであり、それでも警告が必要です。

6
Josh Darnell

これは実際に予想されることです。 this を見てください。

LEFT(character_expression、integer_expression)

Character_expression:

文字またはバイナリデータの式です。 character_expressionには、定数、変数、または列を指定できます。 character_expressionは、textまたはntextを除き、暗黙的にvarcharまたはnvarcharに変換できる任意のデータ型にすることができます。それ以外の場合は、CAST関数を使用して、character_expressionを明示的に変換します。

Col1をVARCHAR(100)に変更すると、警告は消えます。テスト目的でのみ試してください。

0
Danilo Braga