0

私は最適化のテクニックに関する助けを求めています。 in節のテンポラリテーブルを使用すると、クエリが5秒以上実行されます。静的な値を変更すると、1秒未満でデータが返されます。私はこれを最適化する方法を理解しようとしています。SQL Server(2014)プロシージャに渡されたフィルタ変数を使用するときのクエリの最適化

-- details about the number of rows in table 
dept_activity table 

    - total rows    - 17,319,666 
    - rows for (dept_id = 10) - 36054 

-- temp table 
CREATE TABLE #tbl_depts (
    Id INT Identity(1, 1) PRIMARY KEY 
    ,dept_id integer 
); 

-- for example I inserted one row but based on conditions multiple department numbers are inserted in this temp table 
insert into #tbl_depts(dept_id) values(10); 

-- this query takes more than 5 seconds 
SELECT activity_type,count(1) rc 
FROM dept_activity da 
WHERE (
     @filter_by_dept IS NULL 
     OR da.depart_id IN (
      SELECT td.dept_id 
      FROM #tbl_depts td 
      ) 
     ) 
group by activity_type; 

-- this query takes less than 500 milli seconds 
SELECT activity_type,count(1) rc 
FROM dept_activity da 
WHERE (
     @filter_by_dept IS NULL 
     OR da.depart_id IN (
       10 -- changed to static value 
      ) 
     ) 
group by activity_type; 

1秒以内に最初のクエリのデータを返すためにどのように最適化できますか。

+0

このように労働組合を使用する方法について '、ACTIVITY_TYPEを選択filter_by_deptがACTIVITY_TYPE 組合 SELECT ACTIVITY_TYPEでNULL グループですdept_activityダ (1)からのrc をカウントし、カウント(1どのように' – TheGameiswar

+1

questi; ACTIVITY_TYPEによって( SELECTが#tbl_depts FROM をtd.dept_idをTD) グループIN dept_activityダ WHERE da.depart_id FROM)RC あなたのテストデータが大きい場合は、DDL、DMLが含まれている必要があります。テストデータが大きい場合は、テーブルのスキーマと統計をスクリプト化してください( '右クリックデータベース - >スクリプト生成 - >特定のデータベースオブジェクトを選択 - >次の画面で詳細を選択し、統計情報を選択してください) 'をクリックして質問に貼り付けます。この情報を使って、あなたが直面している問題と同じ問題を解決します。その他、質問に答えるのが非常に難しくなります。 – TheGameiswar

答えて

0

これはただ1つの値でテストしていますが、実際のケースとは異なりますか?

ここでオプティマイザが持つ問題は、テンポラリ・ローの数がわからないことです。 table in -clauseが実際に見つかるので、推測をしなければならないでしょうし、おそらくなぜ結果が異なるのでしょうか。推定行数(+ vs actual)を見ると、これに関するいくつかの洞察が得られるかもしれません。

あなたの句は、これだけの基準が含まれている場合:

@filter_by_dept IS NULL OR da.depart_id IN 

あなたがifブロックを使用してロジックを分離した場合に何が起こるかをテストするためには良いかもしれないが、フィルタのすべてのフェッチ1、およびその他へデータ。

option (recompile)の両方をテストしてより良い計画になるかもしれませんが、毎回計画が再生成されるので(少しずつ)多くのCPUを使用します。または、動的SQLを使って句を作成するか(tempテーブルだけでorステートメントを最適化するか、ばかげた量の値がない場合は完全なin節を実行します)、実際には醜いかもしれません。

0

同じことを書くにはさまざまな方法があります。あなたの要件に応じて使用してください -

別のブロック

IF @filter_by_dept IS NULL 
BEGIN 
    SELECT da.activity_type, count(1) rc 
    FROM dept_activity da 
    GROUP BY da.activity_ty 
END 
ELSE 
BEGIN 
    SELECT da.activity_type,COUNT(1) rc 
    FROM dept_activity da 
    INNER JOIN #tbl_depts td ON td.dept_id = da.depart_id 
    GROUP BY da.activity_ty 
END 

を動的問合せ

DECLARE @sql_stmt VARCHAR(5000)  
SET @sql_stmt = ' 
     SELECT activity_type, COUNT(1) rc 
     FROM dept_activity da 
    ' 

IF @filter_by_dept IS NOT NULL 
    SET @sql_stmt = @sql_stmt + ' INNER JOIN #tbl_depts td ON td.dept_id = da.depart_id' 

SET @sql_stmt = @sql_stmt + ' GROUP BY da.activity_type ' 
EXEC(@sql_stmt) 

シンプルな左が比較的 に参加し、それは二つの選択肢の上に遅くなることができます。

SELECT da.activity_type, count(1) rc 
FROM dept_activity da 
LEFT JOIN #tbl_depts td ON td.dept_id = da.depart_id 
WHERE @filter_by_dept IS NULL OR td.id IS NOT NULL 
GROUP BY da.activity_type 
0

最も大きな問題は、「オプションパラメータ」を使用する可能性が最も高いことです。クエリオプティマイザは天気が気にならないか、@filter_by_deptが次回に実行されたときにインデックスを検索するのではなく、インデックススキャンを安全に選択するための値を持っています。 OPTION(RECOMPILE)があなたの友人になれる場所です。特にこのような簡単で簡単なクエリをコンパイルすることができます。 また、INの代わりにWHERE EXISTSを使用することによって潜在的な利益が得られます。

次を試してください...

DECLARE @filter_by_dept INT = 10; 

SELECT 
    da.activity_type, 
    rc = COUNT(1) 
FROM 
    dbo.dept_activity da 
WHERE 
    @filter_by_dept IS NULL 
    OR 
    EXISTS (SELECT 1 FROM #tbl_depts td WHERE da.depart_id = td.dept_id) 
GROUP BY 
    da.activity_type 
OPTION (RECOMPILE); 

HTH、ジェイソン

関連する問題