2011-10-07 64 views
5

我有正在採取一些嚴重的時間上比任何過去,說,小時值數據的較舊的執行查詢。這將創建一個將用於數據挖掘的視圖,所以期望它能夠搜索數週或數月的數據並在合理的時間內返回(即使幾分鐘也沒問題)...我運行的日期範圍爲10/3/2011 12:00pm10/3/2011 1:00pm,花了44分鐘!)加快SQL查詢

問題在於底部有兩個LEFT OUTER JOIN s。當我把它們拿出來時,它可以在大約10秒內運行。但是,這些是這個查詢的麪包和黃油。

這一切都是從一個表來。該查詢返回的結果與原始表格不同的是xweb_rangexweb_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包括:(值)'** – JNK

+2

而且,你知道'CTE's只是一次性的觀點,正確的?沒有性能優勢?你基本上有3個級別的嵌套視圖,你是在這裏聚集...... – JNK

回答

2

這是很難理解您的查詢到底是什麼試圖不知道域名。然而,在我看來,像你的查詢只是試圖找到,在dbo.some_table每一行,其中sub_id不爲0,下面列的記錄範圍內(或者,如果只有一個匹配,即單值):

  • LO_AVG當LO_SENSOR_ALARM = 0
  • LC_AVG當LC_SENSOR_ALARM = 0
  • RO_AVG噹噹RC_SENSOR_ALARM = 0

您構造的本曲RO_SENSOR_ALARM = 0

  • RC_AVG爲每行分配一個連續的行號,將_AVG列及其行號未賦值,按行號計算範圍聚合分組,然後按行號連接回原始記錄。 CTE沒有實現結果(也沒有按照評論中的討論進行索引)。所以每個參考AddRowNumbers是昂貴的,因爲ROW_NUMBER() OVER (ORDER BY LO_Avg)是一種。

    而不是削減該表最多隻是行號加入它重新走到一起的,爲什麼不這樣做:

    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兼容級別設置爲100以外的某個值(SQL 2008)。你也可以用'SELECT LO_AVG,LO_SENSOR_ALARM UNION ALL SELECT LC_AVG,LC_SENSOR_ALARM UNION ALL ...'替代'VALUES ...''' –