T-SQLでCASTを使用したパフォーマンスヒット


12

指定されたフィールドに対して一般的にSQL条件ステートメントを発行するSQLジェネレーターがあります(説明のために、次のようにラベルを付けますmyField)。

myFieldタイプがの場合NVARCHAR、次のように、上記のフィールドと文字列を比較できますmyField = 'foo'

ただし、これはタイプのフィールドでは機能しませんNTEXT。したがって、キャストとの比較を行う必要があります CAST(myField as NVARCHAR(MAX)) = 'foo'。これmyFieldは、タイプがNVARCHARまたはの場合に実際に機能しNTEXTます。

すでにタイプのあるフィールドで前述のキャストを実行すると、パフォーマンスにどのような影響がありNVARCHARますか?私の希望は、SQL ServerがそれmyFieldが既に型であると動的に認識できるほどNVARCHAR効果的になることです(事実上をCAST何もしない状態に変えます)。


この質問を見つけた人への簡単なメモ:NTEXT(およびTEXTとIMAGE)は正式に非推奨になり、SQL Serverの将来のバージョンで削除される予定です(IIRCはSQL1014でも機能します)。したがって、NVARCHR(MAX)を使用する必要があります。 (またはVARCHAR(MAX)またはVARBINARY(MAX))の代わりに。このインスタンスでNTEXT列をNVARCHAR(MAX)の列に置き換えると、キャストを行う必要がなくなります。これは、その型と直接比較できるため、他の場所でも効率が向上する可能性があるためです。残念ながら、*(MAX)列にインデックスを付けることはできませんが、TEXT / NTEXT列にインデックスを付けることはできません。
David Spillett、2014

回答:


12

列のキャストがまったく同じデータ型と長さであり、シーク述語がリテラルである場合、実際にそれを無視するか、no-opとして扱い、同等のインデックスシークを行います。

Seek Keys[1]: Prefix: [tempdb].[dbo].[#test].name = Scalar Operator(N'rpc')

列のキャストが同じデータ型で、長さが長く、シーク述語が文字列リテラルの場合、インデックススキャンが発生します。これは明らかに避けるべきです。

列のキャストが同じデータ型で同じ長さ以上であり、シーク述語がローカル変数である場合、計算スカラー演算子が実行プランに追加されます。GetRangeThroughConvert範囲を呼び出して出力します。

この範囲はインデックスシークを行うために使用され、かなり効率的です

Seek Keys[1]: 
Start: [tempdb].[dbo].[#test].name > Scalar Operator([Expr1006]), 
End: [tempdb].[dbo].[#test].name < Scalar Operator([Expr1007])

テストコード

SELECT *
 INTO #test
  FROM [master].[dbo].[spt_values]

CREATE NONCLUSTERED INDEX [ixname] ON #test
(
    [name] ASC
)

DECLARE @name NVARCHAR(MAX)

SET @name = 'rpc'

SELECT name
FROM #test
WHERE CAST(name AS NVARCHAR(35))= @name --Cast the same and local variable

SELECT name
FROM #test
WHERE CAST(name AS NVARCHAR(MAX))=@name --Cast to longer and local variable

SELECT name
FROM #test
WHERE CAST(name AS NVARCHAR(35))='rpc' --Cast the same and literal

SELECT name
FROM #test
WHERE CAST(name AS NVARCHAR(MAX))='rpc' --Cast to longer and literal

6

CASTMartin Smithの最後の例が示すように、インデックスシークの使用が無効になるため、一般にはパフォーマンスを低下させます。nvarchar(max)異なる長さへのキャストは、異なるデータ型を意味しnvarcharます。それがすべてであるという事実は無関係です。

その上、比較の右側のデータ型も重要です。異なる長さのローカル変数またはパラメーターの場合、一方の側は暗黙的CASTに2つのデータ型のうち最も広いものになります(データ型の優先順位を参照)。

基本的に、もしあなたがそれに将軍CASTを持っているなら、nvarchar(max)物事を混乱させるでしょう。全体ntextを追加CASTする前にの使用を修正することを検討します。

クエリプランに変換が表示されない場合があります。参照してくださいポール・ホワイトさんのブログの記事を


2

ただのメモ、Datecreatedがdatetimeであるこのようなキャスト

 Cast (Datecreated as date) = cast(@MydatetimeValue as date)

インデックスが存在する場合にインデックスを使用するSQLの機能を損なうことはありません。インデックスが存在しない場合、不足しているインデックスがログに記録される可能性があります。

同様に、intto tinyintまたはbigintto intなどにキャストする場合、キャスト操作によって2つの比較可能なデータ型のソート順が変更されないことがオプティマイザにわかっている場合、キャスト関数はSQLによるインデックスの使用を停止しません。

以下は、Adventureworks2008R2を使用して実際の計画を実行および表示できる一連のテストです。

select count(*) from Sales.SalesOrderDetail where SalesOrderID = 8 --1
select top 10 * from Sales.SalesOrderDetail where cast(SalesOrderID as tinyint) = 8  --2
select top 10 * from Sales.SalesOrderDetail where cast(SalesOrderID as bigint) = 8  --3
select top 10 SalesOrderID from Sales.SalesOrderDetail where cast(ModifiedDate  as date) = '19780322' --4
select top 10 SalesOrderID from Sales.SalesOrderDetail where convert(date,ModifiedDate) = '19780322'  --5
select top 10 SalesOrderID from Sales.SalesOrderDetail where cast(ModifiedDate as varchar(20)) = '1978'  --6 -- THIS WILL NOT USE INDEX
select  SalesOrderID from Sales.SalesOrderDetail where cast(ModifiedDate  as date) between '19780101' and '19780109'  --7

1
日付としてキャストするとインデックスシークを実行できますが、キャストなしで範囲シークとして表現する場合と比較すると、問題あります
マーティン・スミス
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.