2011-10-07 14 views
5

私は、過去の、例えば何時間ものデータよりも古いもので実行するために深刻な時間を費やしているクエリを持っています。これはデータマイニングに使用されるビューを作成するため、数週間または数ヶ月のデータを検索して妥当な時間内に返すことができるという期待があります(数分でも問題ありません... 10/3/2011 12:00pm10/3/2011 1:00pmの日付範囲で実行され、44分かかりました!)SQLクエリを高速化する

問題は下の2つのLEFT OUTER JOINです。私はそれらを取るとき、約10秒で実行することができます。しかし、これらはこのクエリのパンとバターです。

これはすべて1つのテーブルから来ています。このクエリが元のテーブルと異なるだけ返すのは、列xweb_rangeです。 xweb_rangeが唯一の彼らは[LO,LC,RO,RC]_Sensor_Alarm = 0に対応する[LO,LC,RO,RC]_Avgからの値を使用する計算フィールドの列(範囲)(センサーアラーム= 1場合、範囲の計算には含まれません)

WITH Alarm (sub_id, 
LO_Avg, LO_Sensor_Alarm, LC_Avg, LC_Sensor_Alarm, RO_Avg, RO_Sensor_Alarm, RC_Avg, RC_Sensor_Alarm) AS (
SELECT sub_id, LO_Avg, LO_Sensor_Alarm, LC_Avg, LC_Sensor_Alarm, RO_Avg, RO_Sensor_Alarm, RC_Avg, RC_Sensor_Alarm 
FROM dbo.some_table 
where sub_id <> '0' 
) 
, AddRowNumbers AS (
SELECT rowNumber = ROW_NUMBER() OVER (ORDER BY LO_Avg) 
    , sub_id 
    , LO_Avg, LO_Sensor_Alarm 
    , LC_Avg, LC_Sensor_Alarm 
    , RO_Avg, RO_Sensor_Alarm 
    , RC_Avg, RC_Sensor_Alarm 
FROM Alarm 
) 
, UnPivotColumns AS (
SELECT rowNumber, value = LO_Avg FROM AddRowNumbers WHERE LO_Sensor_Alarm = 0 
UNION ALL SELECT rowNumber, LC_Avg FROM AddRowNumbers WHERE LC_Sensor_Alarm = 0 
UNION ALL SELECT rowNumber, RO_Avg FROM AddRowNumbers WHERE RO_Sensor_Alarm = 0 
UNION ALL SELECT rowNumber, RC_Avg FROM AddRowNumbers WHERE RC_Sensor_Alarm = 0 
) 
SELECT rowNumber.sub_id 
    , cds.equipment_id 
    , cds.read_time 
    , cds.LC_Avg 
    , cds.LC_Dev 
    , cds.LC_Ref_Gap 
    , cds.LC_Sensor_Alarm 
    , cds.LO_Avg 
    , cds.LO_Dev 
    , cds.LO_Ref_Gap 
    , cds.LO_Sensor_Alarm 
    , cds.RC_Avg 
    , cds.RC_Dev 
    , cds.RC_Ref_Gap 
    , cds.RC_Sensor_Alarm 
    , cds.RO_Avg 
    , cds.RO_Dev 
    , cds.RO_Ref_Gap 
    , cds.RO_Sensor_Alarm 
    , COALESCE(range1.range, range2.range) AS xweb_range 
FROM AddRowNumbers rowNumber 
    LEFT OUTER JOIN (SELECT rowNumber, range = MAX(value) - MIN(value) FROM UnPivotColumns GROUP BY rowNumber HAVING COUNT(*) > 1) range1 ON range1.rowNumber = rowNumber.rowNumber 
    LEFT OUTER JOIN (SELECT rowNumber, range = AVG(value) FROM UnPivotColumns  GROUP BY rowNumber HAVING COUNT(*) = 1) range2 ON range2.rowNumber = rowNumber.rowNumber 
    INNER JOIN dbo.some_table cds 
    ON rowNumber.sub_id = cds.sub_id 
+1

代わりに、行番号で一緒に戻ってそれに参加するだけで、この表を切るのは、なぜ同じような何かをしません。しかし、テーブルスキーマと設定したインデックスに関する情報を投稿する必要があります。 – jadarnel27

+6

最初に最も明白な質問 - ** 'Rownumber INCLUDE(value)'にインデックスがありますか?** – JNK

+2

また、 'CTE'は単なる使い捨てビューであることは知っていますか?パフォーマンス上の利点はありませんか?あなたは基本的にここに集約しているネストされたビューの3つのレベルがあります... – JNK

答えて

2

であるそれはあなたのクエリがある正確に理解するのは難しいですドメインを知らずにやろうとしています。しかし、あなたのクエリが単にdbo.some_tableの各行に対して、sub_idが0でない場合、レコード内の次の列の範囲(または1つだけが一致する場合)を検索しようとしているようです:

  • LO_AVGときLO_SENSOR_ALARM = 0
  • LC_AVGときLC_SENSOR_ALARM = 0
  • RO_AVGときRO_SENSOR_ALARM = 0
  • RC_AVG RC_SENSOR_ALARM = 0

あなたはこのQUを構築各行に順次行番号を割り当て、行番号とともに_AVG列をピボット解除し、行番号で範囲集約グループを計算してから行番号で元のレコードに戻って結合します。 CTEは結果を具体化しません(また、コメントで説明されているように、索引付けもされません)。 ROW_NUMBER() OVER (ORDER BY LO_Avg)は並べ替えであるため、AddRowNumbersへの各参照は高価です。私は、これは許容質問だと思います

SELECT cds.sub_id 
    , cds.equipment_id 
    , cds.read_time 
    , cds.LC_Avg 
    , cds.LC_Dev 
    , cds.LC_Ref_Gap 
    , cds.LC_Sensor_Alarm 
    , cds.LO_Avg 
    , cds.LO_Dev 
    , cds.LO_Ref_Gap 
    , cds.LO_Sensor_Alarm 
    , cds.RC_Avg 
    , cds.RC_Dev 
    , cds.RC_Ref_Gap 
    , cds.RC_Sensor_Alarm 
    , cds.RO_Avg 
    , cds.RO_Dev 
    , cds.RO_Ref_Gap 
    , cds.RO_Sensor_Alarm 

    --if the COUNT is 0, xweb_range will be null (since MAX will be null), if it's 1, then use MAX, else use MAX - MIN (as per your example) 
    , (CASE WHEN stats.[Count] < 2 THEN stats.[MAX] ELSE stats.[MAX] - stats.[MIN] END) xweb_range 

FROM dbo.some_table cds 

    --cross join on the following table derived from values in cds - it will always contain 1 record per row of cds 
    CROSS APPLY 
    (
     SELECT COUNT(*), MIN(Value), MAX(Value) 
     FROM 
     (
      --construct a table using the column values from cds we wish to aggregate 
      VALUES (LO_AVG, LO_SENSOR_ALARM), 
        (LC_AVG, LC_SENSOR_ALARM), 
        (RO_AVG, RO_SENSORALARM), 
        (RC_AVG, RC_SENSOR_ALARM) 


     ) x (Value, Sensor_Alarm) --give a name to the columns for _AVG and _ALARM 
     WHERE Sensor_Alarm = 0 --filter our constructed table where _ALARM=0 

    ) stats([Count], [Min], [Max]) --give our derived table and its columns some names 

WHERE cds.sub_id <> '0' --this is a filter carried over from the first CTE in your example 
+0

これにご協力いただきありがとうございます。私はこの方法を試してみたいですが、VALUESの近くでエラーが発生しています。私はあなたがやっているような何かを見たことがないので、コードのその部分の正しい構文がわからない。この技術とは何ですか? – Tom

+0

SQL Server 2008を使用していますか? 'VALUES'キーワードはテーブル値コンストラクタです。このドキュメントはhttp://technet.microsoft.com/en-us/library/dd776382.aspxにあり、例CのFROM句の派生テーブルと同様の使い方があります。 –

+0

また、 'VALUES'を使用できない場合は、SQL Compatibility Levelが100以外の値(SQL 2008)に設定されている可能性があります。 'VALUES ...'を 'SELECT LO_AVG、LO_SENSOR_ALARM UNION ALL SELECT LC_AVG、LC_SENSOR_ALARM UNION ALL ... 'に置き換えることもできます。 –

関連する問題