2016-07-28 9 views
0

あるテーブルから別のテーブルにデータをコピーしています。コピー中に、ある列を修正するための計算をしています。SQL Serverクエリを最適化する方法

SQL Serverクエリ:

INSERT INTO rat_proj_duration_map_2 
    SELECT 
    r.*, 
    r.hour_val/(CASE 
     WHEN week_val = 1 AND 
     (SELECT TOP 1 
      hrswk 
     FROM UserProfileRATinterface_view us 
     INNER JOIN users u 
      ON u.username = us.username 
     WHERE calwk = 2 
     AND r.uid = u.uid 
     AND yr = 2016) 
     > 0 THEN (SELECT TOP 1 
      hrswk 
     FROM UserProfileRATinterface_view us 
     INNER JOIN users u 
      ON u.username = us.username 
     WHERE calwk = 2 
     AND r.uid = u.uid 
     AND yr = 2016) 
     WHEN (SELECT 
      hrswk 
     FROM UserProfileRATinterface_view us 
     INNER JOIN users u 
      ON u.username = us.username 
     WHERE r.week_val = us.calwk 
     AND r.uid = u.uid 
     AND yr = 2016) 
     < 1 AND 
     (SELECT 
      MAX(hrswk) 
     FROM UserProfileRATinterface_view us 
     INNER JOIN users u 
      ON u.username = us.username 
     WHERE r.uid = u.uid 
     AND yr = 2016) 
     > 0 THEN (SELECT 
      MAX(hrswk) 
     FROM UserProfileRATinterface_view us 
     INNER JOIN users u 
      ON u.username = us.username 
     WHERE r.uid = u.uid 
     AND yr = 2016) 
     WHEN (SELECT 
      COUNT(*) 
     FROM UserProfileRATinterface_view us 
     INNER JOIN users u 
      ON u.username = us.username 
     WHERE r.uid = u.uid 
     AND yr = 2016) 
     <= 0 THEN 1 
     ELSE (SELECT 
      hrswk 
     FROM UserProfileRATinterface_view us 
     INNER JOIN users u 
      ON u.username = us.username 
     WHERE r.week_val = us.calwk 
     AND r.uid = u.uid 
     AND yr = 2016) 
    END) * 100 AS percentage_val 
    FROM rat_proj_duration_map r 

私はこのクエリを実行すると、私は問題を時間を抜け出します。

TCPプロバイダー:タイムアウトエラー[258]

SQL Serverは、タイムアウト値を増やすために私の手ではありません。

SQLクエリを最適化できますか?

+2

質問に実行計画を追加します:https://www.mssqltips.com/sqlservertip/1856/sql-server-query-execution-plans-in-と私にその場合の考え方は次のようになりますSQL Server管理スタジオ/ – Backs

答えて

0

case節のサブクエリは基本的に同じようです。このサブクエリのグループ化されたバージョン(... where yr=2016 group by u.uid)を定義することでコマンド全体を簡素化できます(一般的なテーブル式として使用することをお勧めします)。これにより、多くの重複した操作を潜在的に節約できます。

次はうまくいくかもしれない

(それをテストしていない):

;WITH usrall as (
SELECT u.uid ui, hrswk hw, r.week wk, us.calwk cw 
FROM UserProfileRATinterface_view us 
INNER JOIN users u on u.username=us.username 
WHERE r.uid=u.uid and yr=2016 
), usrgrp as (
SELECT ui gui, MAX(hrswk) ghw, count(*) gcnt FROM usrall group by ui 
), denom as (
SELECT gui dui, COALESCE(MAX(w2.hw), MAX(wkwc.hw), MAX(gwh)) dnm 
FROM usrgrp 
LEFT JOIN usrall w2 ON w2.ui=gui AND w2.cw=2 AND w2.hw>0 
LEFT JOIN usrall wkcw ON wkcw.ui=gui AND wkcw.wk=wkcw.cw AND wkwc.hw<1 
GROUP BY gui 
)   
SELECT r.*, r.hour_val/d.dnm 
FROM rat_proj_duration_map r 
INNER JOIN denom d ON d.dui=u.uid 

は基本的に私は(私はそれが動作願っています: - /)試してみましたが、計算可能な3をチェックしCOALESCE()機能により、ケースの構造を交換します値を1つずつ順番に並べ替えます。最初の非NULL値が受け入れられます。

私が言ったように:私はそれをテストしていません。幸運

0

このクエリは論理的に正しいですか?具体的なORDER BYを持たないいくつかのTOP 1があります。TOPのない副選択のスカラー比較(同じソースの他の副選択でtopを使用している場合、複数の行が返されることがあります)。

はい - このクエリは最適化できます。オリジナルのロジックが正しいかどう

INSERT INTO rat_proj_duration_map_2 
    SELECT 
     r.*, 
     r.hour_val/(CASE 
           WHEN week_val = 1 AND us.min_hrswk_2 > 0 
           THEN us.min_hrswk_2 
           WHEN us.min_hrswk_week_val <1 
           AND max_hrswk > 0 
           THEN max_hrswk 
           WHEN us.cnt <= 0 
           THEN 1 
           ELSE min_hrswk_week_val 
          END) * 100 as percentage_val 
    FROM 
     rat_proj_duration_map r 
    OUTER APPLY 
    (
     SELECT 
      count(*) as cnt, 
      MIN(CASE WHEN calcw = 2 THEN hrswk END) as min_hrswk_2, 
      MIN(CASE WHEN calcw = r.week_val THEN hrswk END) as min_hrswk_week_val, 
      MAX(hrswk) as max_hrswk 
     FROM UserProfileRATinterface_view us 
     inner join users u on u.username=us.username 
     WHERE r.uid=u.uid and yr=2016 
    ) us 

しかし、私は確認することはできません:あなたは、単一の副選択ステートメントに必要なすべての値を取得し、あなたが今持っているrat_proj_duration_mapの行ごとに同じ副選択の複数の実行を回避することができます。

... 
    r.hour_val/COALESCE(NULLIF(us.min_hrswk_2, 0), 
     NULLIF(us.min_hrswk_week_val, 0), NULLIF(max_hrswk, 0), 1) 
...