6

Chúng tôi có một số máy ghi dữ liệu vào cơ sở dữ liệu theo các khoảng thời gian lẻ tẻ. Đối với mỗi bản ghi, tôi muốn có khoảng thời gian giữa bản ghi này và ghi trước đó.Tối ưu ROW_NUMBER() trong SQL Server

tôi có thể làm điều này bằng ROW_NUMBER như sau:

WITH TempTable AS (
    SELECT *, ROW_NUMBER() OVER (PARTITION BY Machine_ID ORDER BY Date_Time) AS Ordering 
    FROM dbo.DataTable 
) 

SELECT [Current].*, Previous.Date_Time AS PreviousDateTime 
FROM TempTable AS [Current] 
INNER JOIN TempTable AS Previous 
    ON [Current].Machine_ID = Previous.Machine_ID 
    AND Previous.Ordering = [Current].Ordering + 1 

Vấn đề là, nó đi thực sự chậm (vài phút trên một bảng với khoảng 10k mục) - Tôi cố gắng tạo indicies riêng về Machine_ID và Date_Time và một chỉ mục tham gia, nhưng không có gì hữu ích.

Có cách nào để viết lại truy vấn này để đi nhanh hơn không?

Trả lời

5

như thế nào so sánh với phiên bản này ?:

SELECT x.* 
    ,(SELECT MAX(Date_Time) 
     FROM dbo.DataTable 
     WHERE Machine_ID = x.Machine_ID 
      AND Date_Time < x.Date_Time 
    ) AS PreviousDateTime 
FROM dbo.DataTable AS x 

Hoặc phiên bản này ?:

SELECT x.* 
    ,triang_join.PreviousDateTime 
FROM dbo.DataTable AS x 
INNER JOIN (
    SELECT l.Machine_ID, l.Date_Time, MAX(r.Date_Time) AS PreviousDateTime 
    FROM dbo.DataTable AS l 
    LEFT JOIN dbo.DataTable AS r 
    ON l.Machine_ID = r.Machine_ID 
     AND l.Date_Time > r.Date_Time 
    GROUP BY l.Machine_ID, l.Date_Time 
) AS triang_join 
ON triang_join.Machine_ID = x.Machine_ID 
    AND triang_join.Date_Time = x.Date_Time 

Cả hai sẽ hoạt động tốt nhất với chỉ mục trên Machine_ID, Date_Time và cho kết quả chính xác, tôi giả định rằng đây là duy nhất.

Bạn chưa đề cập đến những gì bị ẩn trong * và đôi khi có thể có nghĩa là rất nhiều kể từ khi Machine_ID, chỉ số Date_Time sẽ không bao gồm và nếu bạn có nhiều cột ở đó hoặc có nhiều dữ liệu, ...

+0

Truy vấn thứ hai kết thúc sau vài giây thay vì phút, nhưng truy vấn đầu tiên thực thi nhanh hơn tôi có thể. Hoàn hảo - cảm ơn! –

7

Các ROW_NUMBER nhất định() phân vùng và trật tự đòi hỏi một chỉ mục trên (Machine_ID, Date_Time) để đáp ứng trong một pass:

CREATE INDEX idxMachineIDDateTime ON DataTable (Machine_ID, Date_Time); 

chỉ số riêng biệt trên Machine_ID và DATE_TIME sẽ giúp ít, nếu có.

+0

Như tôi đã nói, tôi cũng đã tạo chỉ mục đó và hoàn toàn không cải thiện hiệu suất truy vấn. –

+4

Đó là vì bạn * kích hoạt điểm đến chỉ mục. Hạn chế nó chỉ để các cột cần thiết và sử dụng bao gồm làm cho các chỉ số không nhóm bao gồm. Nếu quá nhiều cột là cần thiết, sau đó nó phải được thay đổi thành một chỉ số nhóm, với tất cả các hậu quả. –

+0

Bạn có vẻ đúng, việc xóa * giảm thời gian truy vấn chỉ trong vài giây. Tôi không thể hình dung được tại sao điều này lại xảy ra - bạn có thể cung cấp bất kỳ liên kết nào tới điểm * điểm đến * không? –

0

Điều gì sẽ xảy ra nếu bạn sử dụng trình kích hoạt để lưu dấu thời gian cuối cùng trừ đi mỗi lần để nhận sự khác biệt?

+0

Thật không may, đó là dữ liệu lịch sử và không phải lúc nào cũng được thêm vào theo thứ tự. –

2

Tôi đã gặp một số vấn đề về hiệu năng lạ khi sử dụng CTE trong SQL Server 2005. Trong nhiều trường hợp, thay thế CTE bằng bảng tạm thời đã giải quyết được sự cố.

Tôi sẽ thử điều này trước khi tiếp tục với CTE.

Tôi chưa bao giờ tìm thấy bất kỳ lời giải thích nào về các vấn đề về hiệu năng tôi đã thấy và thực sự không có thời gian để tìm hiểu nguyên nhân gốc rễ. Tuy nhiên tôi luôn nghi ngờ rằng động cơ không thể tối ưu hóa CTE theo cùng một cách mà nó có thể tối ưu hóa một bảng tạm thời (có thể được lập chỉ mục nếu cần tối ưu hóa thêm).

Cập nhật

Sau khi bình luận của bạn rằng đây là một cái nhìn, tôi sẽ lần đầu tiên thử nghiệm các truy vấn với một bảng temp để xem nếu đó thực hiện tốt hơn.

Nếu có, và sử dụng một proc được lưu trữ không phải là một lựa chọn, bạn có thể xem xét làm cho CTE hiện tại trở thành một khung nhìn được lập chỉ mục/vật hoá. Bạn sẽ muốn đọc về chủ đề này trước khi đi xuống con đường này, vì liệu đây có phải là ý tưởng hay không tùy thuộc vào nhiều yếu tố, không ít nhất là tần suất dữ liệu được cập nhật.

+0

Tôi sẽ làm như thế nào? Tôi có cần phải thay thế chế độ xem bằng Sproc (vì lượt xem không thể có biến) không? –

+0

Có, tôi không rõ ràng đó là chế độ xem từ câu hỏi của bạn. Xem bản cập nhật cho câu trả lời của tôi (sẽ tiếp tục sau vài phút). –

0

Nếu bạn yêu cầu dữ liệu này thường xuyên, thay vì tính toán dữ liệu mỗi khi bạn kéo dữ liệu, tại sao không thêm cột và tính/điền nó bất cứ khi nào hàng được thêm?

(chỉ số hợp chất Remus' sẽ làm cho các truy vấn nhanh;. Chạy nó một lần duy nhất nên làm cho nó nhanh hơn vẫn)

4

Nếu số lượng hàng trong dbo.DataTable là lớn thì có khả năng là bạn đang gặp vấn đề do CTE tự tham gia vào chính nó.Có một bài đăng trên blog giải thích sự cố trong một số chi tiết here

Thỉnh thoảng trong trường hợp này, tôi phải tạo bảng tạm thời để chèn kết quả truy vấn CTE vào và sau đó thực hiện các kết nối với bảng tạm thời đó (mặc dù điều này có thường được cho trường hợp một số lượng lớn tham gia chống lại các bảng tạm thời được yêu cầu - trong trường hợp của một gia sự khác biệt hiệu suất đơn sẽ ít được chú ý)

+1

Tôi là phương pháp thứ hai này. CTE chỉ đơn giản là viết lại nội tuyến. Cũng giống như lặp lại mã của riêng bạn và tự tham gia, không có gì để đảm bảo rằng trình tối ưu hóa sẽ đẩy nó vào một bảng tạm thời. Nếu bạn đặt mọi thứ trong bảng của riêng mình, bạn có thể chọn chỉ mục và/hoặc tránh làm việc gấp đôi. Có nói rằng, tôi sử dụng CTEs nơi bảo trì mã là quan trọng và nơi mà các lược đồ có trách nhiệm thay đổi rất nhanh chóng (hoặc trong quan điểm, như trường hợp này). –

Các vấn đề liên quan