web-dev-qa-db-ja.com

暗黙的な変換に関する警告

2つの名前列を持つテーブルがあります。

CREATE TABLE Test
(
  TestID int identity primary key clustered
  , Name_Eng nvarchar(50)
  , Name_Nat nvarchar(50)
)

このName列を,で区切って取得するには、次のようにクエリが必要です。

    DECLARE @NameColumns NVARCHAR(1024)

            SET @NameColumns = STUFF(
      (SELECT   ',' + 'Test.' + name AS [text()]
       FROM     ( SELECT    c.name
                  FROM      sys.columns c 
                            INNER JOIN sys.tables t ON t.object_id = c.object_id
                  WHERE     t.name = 'Test'
                            AND c.name LIKE 'Name_%'
                ) AS D
                FOR                  XML PATH('') ,
                                         TYPE).value('.[1]', 'VARCHAR(MAX)'), 1, 1,
                                     N'')

select  @NameColumns

ただし、このクエリの実行プランには警告があります。

implicit conversion warning

この警告を削除する方法はありますか?

XML関数value()が原因で警告が表示されます。 value()の2番目のパラメーターは、XMLに格納されている値の変換先です。これは実際に暗黙的な変換ではなく、発生を要求しているため、非常に明示的な変換であると主張できます。おそらく、接続アイテムがMicrosoftに提案するものです。

あなたが見るものを再現する最も簡単な方法。

_declare @X xml;
select @X.value('text()[1]', 'int');
_

これらの2つの警告を表示します。

_<Warnings>
    <PlanAffectingConvert ConvertIssue="Cardinality Estimate" Expression="CONVERT_IMPLICIT(int,XML Reader with XPath filter.[lvalue],0)" />
    <PlanAffectingConvert ConvertIssue="Cardinality Estimate" Expression="CONVERT_IMPLICIT(int,XML Reader with XPath filter.[value],0)" />
</Warnings>
_

ご覧のとおり、intでも同様に取得し、value()を呼び出すたびに2つ取得します。

_declare @X xml;
select @X.value('text()[1]', 'int'),
       @X.value('text()[1]', 'bit');
_
_<Warnings>
    <PlanAffectingConvert ConvertIssue="Cardinality Estimate" Expression="CONVERT_IMPLICIT(int,XML Reader with XPath filter.[lvalue],0)" />
    <PlanAffectingConvert ConvertIssue="Cardinality Estimate" Expression="CONVERT_IMPLICIT(int,XML Reader with XPath filter.[value],0)" />
    <PlanAffectingConvert ConvertIssue="Cardinality Estimate" Expression="CONVERT_IMPLICIT(bit,XML Reader with XPath filter.[lvalue],0)" />
    <PlanAffectingConvert ConvertIssue="Cardinality Estimate" Expression="CONVERT_IMPLICIT(bit,XML Reader with XPath filter.[value],0)" />
</Warnings>
_

このような値を計算するStream Aggregateオペレーターで変換が行われます。

_MIN(CASE WHEN [@X] IS NULL 
      THEN NULL 
      ELSE 
      CASE WHEN datalength(XML Reader with XPath filter.[value])>=(128) 
        THEN CONVERT_IMPLICIT(int,XML Reader with XPath filter.[lvalue],0) 
        ELSE CONVERT_IMPLICIT(int,XML Reader with XPath filter.[value],0) 
      END 
    END)
_

テーブル値関数の結果は、lvalueまたはvalue列に返されます。式は、datalengthを使用してチェックし、どこからフェッチするかを確認してから、目的のデータ型に変換します。

この警告を削除する方法はありますか?

はいあります。 _FOR XML PATH_ステートメントからTYPEディレクティブを削除し、value()関数の呼び出しを削除します。そうすることの副作用は、_&<>_のようなXMLでエンコードする必要がある文字を含む連結した値が結果でエンコードされることです。

9
Mikael Eriksson

@Kinのデータ型については同意しますが、この警告はあなたが思っているほど厄介ではないと思います。いずれにしても、変換よりも桁違いにコストがかかるグループ化された連結を実行しています(そして、Danielが言ったように、物理メモリよりも大きいなど、カタログビューが大量でない限り、それは、見積もり)。

私はvarchar文字列(文字列リテラルのNプレフィックスを省略しないことも意味する)を使用しないように注意しながらクエリをこのように記述します ステートメントターミネータを使用することを保証

_DECLARE @NameColumns nvarchar(max); -- why 1024 when you use max below?

SET @NameColumns = STUFF(
  (SELECT   N',Test.' + name AS [text()] FROM    
    (
      SELECT c.name FROM sys.columns AS c 
        INNER JOIN sys.tables AS t 
          ON t.object_id = c.object_id
        WHERE t.name = N'Test'
        AND   c.name LIKE N'Name_%'
    ) AS D FOR XML PATH(N''),
    TYPE).value(N'.[1]', N'nvarchar(max)'), 1, 1, N'');

SELECT @NameColumns;
_

それでも、Mikaelが提案したようにTYPE/value()を回避して出力を変更せずに暗黙的な変換を回避する方法はないと思います。これがクエリのパフォーマンスに重大な影響を与えることを実際に証明できる場合、私はこれについて心配するでしょう。私のテストでは、2つの異なる形式は同じように動作しました(毎回10ミリ秒未満で話しています)が、もちろん_Sales & Stuff_という名前のテーブルがある場合、暗黙的な変換なしで_Sales &amp; Stuff_になります。

9
Aaron Bertrand

クエリプランの警告は、暗黙的なデータ型変換があるため、SQL Serverが返される行の正しい数を正確に推測できず、その結果mightがより小になることを意味します-最適な計画。

これは通常、大量のデータを処理するため、適切に実行する必要があるクエリでは重要ですが、sys.tablessys.columnsをクエリしているだけなので、状況によってはそうではないようです。

あなたの質問への短い答えは、データベースに数百万、数百万のテーブルと列がなければ、問題ではありません。

3