2011-10-07 14 views
5

Tôi có một truy vấn đang dành một số thời gian nghiêm trọng để thực hiện trên bất kỳ dữ liệu nào cũ hơn quá khứ, ví dụ như số giờ dữ liệu. Điều này sẽ tạo ra một khung nhìn sẽ được sử dụng để datamining, do đó, kỳ vọng là nó sẽ có thể tìm kiếm tuần hoặc tháng của dữ liệu và trở lại trong một khoảng thời gian hợp lý (thậm chí một vài phút là tốt ... Tôi chạy trong phạm vi ngày của 10/3/2011 12:00pm đến 10/3/2011 1:00pm và mất 44 phút!)tăng tốc Truy vấn SQL

Vấn đề là với hai số LEFT OUTER JOIN s ở dưới cùng. Khi tôi lấy chúng ra, nó có thể chạy trong khoảng 10 giây. Tuy nhiên, đó là bánh mì và bơ của truy vấn này.

Tất cả điều này đến từ một bảng. Điều duy nhất truy vấn này trả về khác với bảng gốc là cột xweb_range. xweb_range là một cột lĩnh vực tính toán (range) mà sẽ chỉ sử dụng các giá trị từ [LO,LC,RO,RC]_Avg nơi họ tương ứng [LO,LC,RO,RC]_Sensor_Alarm = 0 (không bao gồm trong tính toán phạm vi nếu báo động cảm biến = 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

Tôi nghĩ rằng đây là một câu hỏi có thể chấp nhận. Tuy nhiên, bạn cần phải đăng lược đồ bảng và thông tin về bất kỳ chỉ mục nào bạn đã thiết lập. – jadarnel27

+6

Câu hỏi rõ ràng nhất đầu tiên - ** Có chỉ mục trên 'Rownumber INCLUDE (giá trị)'? ** – JNK

+2

Ngoài ra, bạn biết 'CTE' s chỉ là một lần xem, phải không? Không có lợi ích hiệu suất? Về cơ bản, bạn có 3 cấp độ xem lồng nhau mà bạn đang tổng hợp ở đây ... – JNK

Trả lời

2

Thật khó để hiểu chính xác những gì truy vấn của bạn là cố gắng làm mà không biết miền. Tuy nhiên, dường như với tôi, truy vấn của bạn chỉ đơn giản là tìm kiếm, đối với mỗi hàng trong dbo.some_table trong đó sub_id không bằng 0, phạm vi của các cột sau trong bản ghi (hoặc, nếu chỉ một cột phù hợp, giá trị đơn đó):

  • LO_AVG khi LO_SENSOR_ALARM = 0
  • LC_AVG khi LC_SENSOR_ALARM = 0
  • RO_AVG khi RO_SENSOR_ALARM = 0
  • RC_AVG khi RC_SENSOR_ALARM = 0

Bạn xây dựng qu này ery gán mỗi hàng một số hàng tuần tự, không phân tách các cột _AVG cùng với số hàng của chúng, tính toán nhóm tổng hợp theo số hàng và sau đó tham gia trở lại bản ghi gốc theo số hàng. CTE không thực hiện các kết quả (cũng không được lập chỉ mục, như được thảo luận trong các nhận xét). Vì vậy, mỗi tham chiếu đến AddRowNumbers là tốn kém, bởi vì ROW_NUMBER() OVER (ORDER BY LO_Avg) là một loại.

Thay vì cắt bảng này lên chỉ để tham gia nó trở lại với nhau bởi số lượng hàng, tại sao không làm một cái gì đó như:

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

Cảm ơn bạn đã nỗ lực về điều này. Tôi muốn thử phương pháp này, tuy nhiên tôi nhận được một lỗi gần VALUES. Tôi không chắc cú pháp chính xác cho phần mã đó như tôi chưa bao giờ thấy một cái gì đó như bạn đang làm. Kỹ thuật này được gọi là gì? – Tom

+0

Bạn đang sử dụng SQL Server 2008? Từ khóa 'VALUES' là Trình tạo giá trị bảng. Tài liệu có tại đây: http://technet.microsoft.com/en-us/library/dd776382.aspx và bạn có thể thấy một cách sử dụng tương tự như một bảng dẫn xuất trong mệnh đề 'FROM' trong Ví dụ C. –

+0

Ngoài ra, nếu bạn không thể sử dụng 'GIÁ TRỊ ', có thể là do Mức Tương thích SQL của bạn được đặt thành một số giá trị khác ngoài 100 (SQL 2008). Bạn cũng có thể thay thế 'VALUES ...' bằng 'SELECT LO_AVG, LO_SENSOR_ALARM UNION ALL SELECT LC_AVG, LC_SENSOR_ALARM UNION ALL ...' –