web-dev-qa-db-ja.com

クエリは高速に実行されますが、ストアドプロシージャでは低速に実行されます

SQL 2005プロファイラーを使用していくつかのテストを行っています。

1つのSQLクエリを実行するだけのストアドプロシージャがあります。

ストアドプロシージャを実行すると、時間がかかり、800,000のディスク読み取りが実行されます。

ストアドプロシージャとは別に同じクエリを実行すると、14,000のディスク読み取りが実行されます。

OPTION(recompile)で同じクエリを実行すると、800,000のディスク読み取りが必要になることがわかりました。

これから、ストアドプロシージャが毎回再コンパイルされ、それが問題の原因であるという(おそらくエラーのある)仮定を立てます。

誰もこれに光を当てることができますか?

ARITHABORTをオンに設定しました。 (これはstackoverflowで同様の問題を解決しましたが、私のものは解決しませんでした)

ストアドプロシージャ全体を次に示します。

CREATE PROCEDURE [dbo].[GET_IF_SETTLEMENT_ADJUSTMENT_REQUIRED]
 @Contract_ID int,
 @dt_From smalldatetime,
 @dt_To smalldatetime,
 @Last_Run_Date datetime
AS
BEGIN
 DECLARE @rv int


 SELECT @rv = (CASE WHEN EXISTS
 (
  select * from 
  view_contract_version_last_volume_update
  inner join contract_version
  on contract_version.contract_version_id = view_contract_version_last_volume_update.contract_version_id
  where contract_version.contract_id=@Contract_ID
  and volume_date >= @dt_From
  and volume_date < @dt_To
  and last_write_date > @Last_Run_Date
 )
 THEN 1 else 0 end)

 -- Note that we are RETURNING a value rather than SELECTING it.
 -- This means we can invoke this function from other stored procedures
 return @rv
END

この問題を示すスクリプトを次に示します。

DECLARE 
 @Contract_ID INT,
 @dt_From smalldatetime,
 @dt_To smalldatetime,
 @Last_Run_Date datetime,
    @rv int


SET @Contract_ID=38
SET @dt_From='2010-09-01'
SET @dt_To='2010-10-01'
SET @Last_Run_Date='2010-10-08 10:59:59:070'


-- This takes over fifteen seconds
exec GET_IF_SETTLEMENT_ADJUSTMENT_REQUIRED @Contract_ID=@Contract_ID,@dt_From=@dt_From,@dt_To=@dt_To,@Last_Run_Date=@Last_Run_Date

-- This takes less than one second!
SELECT @rv = (CASE WHEN EXISTS
(
 select * from 
 view_contract_version_last_volume_update
 inner join contract_version
 on contract_version.contract_version_id = view_contract_version_last_volume_update.contract_version_id
 where contract_version.contract_id=@Contract_ID
 and volume_date >= @dt_From
 and volume_date < @dt_To
 and last_write_date > @Last_Run_Date
)
THEN 1 else 0 end)


-- With recompile option. Takes 15 seconds again!
SELECT @rv = (CASE WHEN EXISTS
(
 select * from 
 view_contract_version_last_volume_update
 inner join contract_version
 on contract_version.contract_version_id = view_contract_version_last_volume_update.contract_version_id
 where contract_version.contract_id=@Contract_ID
 and volume_date >= @dt_From
 and volume_date < @dt_To
 and last_write_date > @Last_Run_Date
)
THEN 1 else 0 end) OPTION(recompile)
34
Andrew Shepherd

OK、以前にもこのような問題がありました。

これを修正する方法は、SP内でローカルパラメータを作成することでした。

DECLARE @LOCAL_Contract_ID int, 
        @LOCAL_dt_From smalldatetime, 
        @LOCAL_dt_To smalldatetime, 
        @LOCAL_Last_Run_Date datetime

SELECT  @LOCAL_Contract_ID = @Contract_ID, 
        @LOCAL_dt_From = @dt_From, 
        @LOCAL_dt_To = @dt_To, 
        @LOCAL_Last_Run_Date = @Last_Run_Date

次に、渡されたパラメーターではなく、SP内のローカルパラメーターを使用します。

通常、これにより当社の問題は修正されました。

これはパラメータスニッフィングが原因であると考えていますが、証拠はありません。申し訳ありませんが... X-)

編集:

洞察に満ちた例、説明、修正については、 SQL Serverパラメータースニッフィングを修正するさまざまなアプローチ をご覧ください。

73
Adriaan Stander

他の人が述べたように、これは「パラメータ探知」の問題である可能性があります。次の行を含めてみてください。

OPTION (RECOMPILE)

sQLクエリの最後。

パラメータースニッフィングとは何かを説明する記事がここにあります: http://blogs.technet.com/b/mdegre/archive/2012/03/19/what-is-parameter-sniffing.aspx

6
ninjaPixel

これは parametersniffing。 が原因だと思います

5
Giorgi

バッチがSQLストアドプロシージャ内で永久に実行されるのにSSMSで即座に実行される理由は、SQLパラメータースニッフィング、特に日時パラメーターで発生します。

パラメータスニッフィングに関する優れた記事がいくつかあります。

そのうちの1つです(書きませんでしたが、ただ渡しました)。

http://www.sommarskog.se/query-plan-mysteries.html

4
user1472721

私の問題で私が実行しました:

exec sp_updatestats 

これにより、spが120秒から3秒にスピードアップします。統計の更新の詳細については、こちらをご覧ください https://msdn.Microsoft.com/en-us/library/ms173804.aspx

3
alaneo

私も今日同じ問題を抱えています。 SPを削除して再作成しましたが、機能しました。これはSPキャッシュを使用するもので、ドロップするとSPキャッシュされたプランが削除されます。同じことを試すか、「DBCC FREEPROCCACHE」を使用してキャッシュを削除できます。

2
SumanKumar