2016-07-25 5 views
2

私は約1,000万行の大きなテーブルで検索しています。私は開始日と終了日を指定し、それらの日付の間に作成されたテーブル内のすべてのレコードを返したいと思います。作成したDATETIME列のDATETIME検索述語が文字列のリテラル述語よりもはるかに遅い

declare @StartDateTime datetime = '2016-06-21', 
     @EndDateTime datetime = '2016-06-22'; 

select * 
FROM Archive.dbo.Order O WITH (NOLOCK) 
where O.Created >= @StartDateTime 
    AND O.Created < @EndDateTime; 

は、非クラスタ化インデックスを持つDATETIME列です:

それはストレートなクエリです。

このクエリには約15秒かかりました。

declare @StartDateTime datetime = '2016-06-21', 
     @EndDateTime datetime = '2016-06-22'; 

select * 
FROM Archive.dbo.Order O WITH (NOLOCK) 
where O.Created >= '2016-06-21' 
    AND O.Created < @EndDateTime; 

が唯一の変更は、文字列リテラルで@StartDateTime検索述語を交換されて次のように私は、少しのクエリを変更した場合

はしかし、それは同じ結果を返すことだけ1秒かかります。実行計画を見て、私が@StartDateTimeを使用したときにインデックススキャンを行いましたが、文字列リテラルを使用したときにインデックスシークを行い、15倍高速でした。

誰もがなぜ文字列リテラルを使用するのがとても速いのか知っていますか?

DATETIME列とDATETIME変数の比較は、列を日付の文字列表現と比較するよりも速いと考えていました。私は作成された列のインデックスを削除して再作成しようとしましたが、それは何の違いもありませんでした。私はテストシステムと同じようにプロダクションシステムで同様の結果を得ているので、奇妙な動作は特定のデータベースやSQL Serverインスタンスに固有のように見えません。

+1

答えはSQL Serverがクエリで変数をどのように認識するかに関係しています。 SQLがクエリを実行する前に変数が何であるかを知ることができない場合、キャッシュされたプランを使用することができないか、新しいものを効果的に推測できない可能性があります。 –

+1

件名にSQLmagの有益な記事があります。[SQLmag最適化変数とパラメータ](http://m.sqlmag.com/t-sql/optimizing-variables-and-parameters) –

+0

@clifton_h:申し訳ありませんが、それはしばらく時間がかかりました。あなたがリンクしたその記事は私の質問に答えます。あなたのコメントを答えに変えたいなら、私はそれを受け入れます。 –

答えて

1

すべての変数には、認識されるインスタンスがあります。

OOP言語では、我々は、通常のキーワードを使用して、一時的な変数からstatic/constant変数間の区別、または変数は、そのインスタンスの内部変数は定数として扱われる関数に呼び出されたときに機能が可変することを変換した場合、 C++に次のような:

SQL Server
void string MyFunction(string& name) 
//technically, `&` calls the actual location of the variable 
//instead of using a logical representation. The concept is the same. 

、標準は少し違っそれを実装することを選びました。そこにはconstantデータの種類がありませんので、代わりに私たちは(システムキーワードとして呼び出しに類似した優先順位を持つ)のいずれか

  • オブジェクト名(「含む、[])オブジェクトdeliminatorと
  • 名前であるリテラルを使用します
  • またはデリミネーター付きの文字列CHAR(39)( ')。

これは、あなたがこれらの変数はSQL Serverはすでに、それは事前に実行パスです選択していることを意味オプティマイザに定数ではありませんので、2つのクエリは、異なる結果をもたらすことに気づいた理由です。

実際の実行計画(CTRL + M)を含めて、SSMSがインストールされている場合は、SELECT文で推定行が何であるかを確認します。これは実行計画のハイライトです。見積もりと実際の行の差が大きいほど、クエリで最適化を使用する可能性が高くなります。あなたの例では、SQL Serverはいくつの行を推測しなければならず、結果をオーバーシュートして効率を失いました。

解決方法は同じですが、必要に応じてすべてをカプセル化することもできます。私たちは、この例ではAdventureWorks2012を使用します。

1)Procedure

CREATE PROC dbo.TEST1 (@NameStyle INT, @FirstName VARCHAR(50)) 
AS 
BEGIN 
    SELECT * 
    FROM Person.PErson 
    WHERE FirstName = @FirstName 
    AND NameStyle = @NameStyle; --namestyle is 0 
END 

2で変数を宣言する)どちらのプランが同じ結果を生成しますDynamic SQL

CREATE PROC dbo.TEST2 (@NameStyle INT) 
AS 
BEGIN 

DECLARE @Name NVARCHAR(50) = N'Ken'; 
DECLARE @String NVARCHAR(MAX) 
SET @String = 
    N'SELECT * 
    FROM Person.PErson 
    WHERE FirstName = @Other 
    AND NameStyle = @NameStyle'; 
EXEC sp_executesql @String 
      , N'@Other VARCHAR(50), @NameStyle INT' 
      , @Other = @Name 
      , @NameStyle = @NameStyle  
END 

に変数を渡します。どちらの場合も、インスタンスのレベルが一定値に変数を変換するためにSQL Serverを許可する方法

お知らせ(意味を私は自分自身でEXECを使用することもできましたが、sp_executesqlは全体のselect文(プラス、その多くのSQL Injection安全)をキャッシュすることができますオブジェクトが設定値で入力された場合)、オプティマイザは利用可能な最も効率的な実行計画を選択することができました。

-- Remove Procs 
DROP PROC dbo.TEST1 
DROP PROC dbo.TEST2 

素晴らしい記事はOPのコメントセクションで強調表示されましたが、あなたはここでそれを見ることができます:Optimizing Variables and Parameters - SQLMAG

関連する問題