2009-09-04 36 views
5

Tôi đang cố gắng thiết lập một số dữ liệu để tính toán nhiều trung vị trong SQL Server 2008, nhưng tôi đang gặp sự cố về hiệu suất. Hiện tại, tôi đang sử dụng số điện thoại pattern này ([ví dụ khác bottom). Có, tôi không sử dụng một CTE, nhưng bằng cách sử dụng một sẽ không khắc phục được vấn đề tôi đang có anyways và hiệu suất là người nghèo vì các truy vấn phụ row_number chạy trong nối tiếp, không song song.Nhiều Row_Number() Các cuộc gọi trong một truy vấn SQL đơn

Dưới đây là ví dụ đầy đủ. Bên dưới SQL tôi giải thích vấn đề hơn.

-- build the example table  

CREATE TABLE #TestMedian (
    StateID INT, 
    TimeDimID INT, 
    ConstructionStatusID INT, 

    PopulationSize BIGINT, 
    SquareMiles BIGINT 
); 

INSERT INTO #TestMedian (StateID, TimeDimID, ConstructionStatusID, PopulationSize, SquareMiles) 
VALUES (1, 1, 1, 100000, 200000); 

INSERT INTO #TestMedian (StateID, TimeDimID, ConstructionStatusID, PopulationSize, SquareMiles) 
VALUES (1, 1, 1, 200000, 300000); 

INSERT INTO #TestMedian (StateID, TimeDimID, ConstructionStatusID, PopulationSize, SquareMiles) 
VALUES (1, 1, 1, 300000, 400000); 

INSERT INTO #TestMedian (StateID, TimeDimID, ConstructionStatusID, PopulationSize, SquareMiles) 
VALUES (1, 1, 1, 100000, 200000); 

INSERT INTO #TestMedian (StateID, TimeDimID, ConstructionStatusID, PopulationSize, SquareMiles) 
VALUES (1, 1, 1, 250000, 300000); 

INSERT INTO #TestMedian (StateID, TimeDimID, ConstructionStatusID, PopulationSize, SquareMiles) 
VALUES (1, 1, 1, 350000, 400000); 

--TruNCATE TABLE TestMedian 

    SELECT 
     StateID 
     ,TimeDimID 
     ,ConstructionStatusID 
     ,NumberOfRows = COUNT(*) OVER (PARTITION BY StateID, TimeDimID, ConstructionStatusID) 
     ,PopulationSizeRowNum = ROW_NUMBER() OVER (PARTITION BY StateID, TimeDimID, ConstructionStatusID ORDER BY PopulationSize) 
     ,SquareMilesRowNum = ROW_NUMBER() OVER (PARTITION BY StateID, TimeDimID, ConstructionStatusID ORDER BY SquareMiles) 
     ,PopulationSize 
     ,SquareMiles 
    INTO #MedianData 
    FROM #TestMedian 

    SELECT MinRowNum = MIN(PopulationSizeRowNum), MaxRowNum = MAX(PopulationSizeRowNum), StateID, TimeDimID, ConstructionStatusID, MedianPopulationSize= AVG(PopulationSize) 
    FROM #MedianData T 
    WHERE PopulationSizeRowNum IN((NumberOfRows + 1)/2, (NumberOfRows + 2)/2) 
    GROUP BY StateID, TimeDimID, ConstructionStatusID 

    SELECT MinRowNum = MIN(SquareMilesRowNum), MaxRowNum = MAX(SquareMilesRowNum), StateID, TimeDimID, ConstructionStatusID, MedianSquareMiles= AVG(SquareMiles) 
    FROM #MedianData T 
    WHERE SquareMilesRowNum IN((NumberOfRows + 1)/2, (NumberOfRows + 2)/2) 
    GROUP BY StateID, TimeDimID, ConstructionStatusID 


    DROP TABLE #MedianData 
    DROP TABLE #TestMedian 

Vấn đề với truy vấn này là SQL Server thực hiện cả hai truy vấn con "ROW__NUMBER() OVER ..." theo thứ tự, không song song. Vì vậy, nếu tôi có 10 trong số các phép tính ROW__NUMBER này, nó sẽ tính toán chúng sau cái kia và tôi nhận được sự tăng trưởng tuyến tính. Tôi có một hệ thống 8-bit 32 GB Tôi đang chạy truy vấn này và tôi sẽ yêu một số tính song song. Tôi đang cố gắng chạy loại truy vấn này trên một bảng hàng 5.000.000.

Tôi có thể nói điều này bằng cách xem xét kế hoạch truy vấn và thấy các sắp xếp trong cùng một đường dẫn thực hiện (hiển thị XML của kế hoạch truy vấn sẽ không hoạt động tốt trên SO).

Vì vậy, câu hỏi của tôi là: Làm thế nào tôi có thể thay đổi truy vấn này để các truy vấn ROW_NUMBER được thực thi song song? Có một kỹ thuật hoàn toàn khác mà tôi có thể sử dụng để chuẩn bị dữ liệu cho nhiều phép tính trung bình không?

+0

+1, đủ mã để thử trên hệ thống của tôi !! –

+0

+1, vì tôi không biết bạn có thể sử dụng các mệnh đề OVER bên ngoài các hàm xếp hạng - trong SQL 2005 cũng vậy, không kém. Woot! –

+0

Philip: Đối với các hàm tổng hợp bình thường, chỉ có mệnh đề PARTITION BY, chứ không phải phần ORDER BY :-( – RBarryYoung

Trả lời

2

Mỗi ROW_NUMBER yêu cầu sắp xếp các hàng. Vì hai RN của bạn có các điều kiện ORDER BY khác nhau, truy vấn phải tạo ra kết quả, sau đó đặt nó cho các RN đầu tiên (nó có thể được sắp xếp trước), tạo RN, sau đó đặt nó cho RN thứ hai và tạo ra kết quả RN thứ hai. Chỉ đơn giản là không có bất kỳ bụi ma thuật pixie nào có thể thực hiện một giá trị số hàng mà không đếm vị trí của hàng theo thứ tự bắt buộc.

+0

Tôi hiểu rằng không có bụi ma thuật pixie có sẵn, có một sự thiếu hụt trên toàn thế giới. :) Tôi biết rằng nó không thể tìm ra những gì RN là w/o đầu tiên đặt hàng nó. Làm thế nào tôi có thể thiết lập nó để nó đặt hàng nó theo những cách khác nhau song song với calc RN? Có một kỹ thuật để chia nó thành nhiều truy vấn và sau đó tham gia các bộ kết quả? Tôi không kết hôn với việc sử dụng kiểu RN, vì vậy mọi ý tưởng mang tính xây dựng sẽ được đánh giá cao. Tôi không thể là người đầu tiên trên thế giới muốn lấy một bộ dữ liệu và tính toán nhiều trung vị cùng một lúc một cách hiệu quả! Để làm điều đó, dữ liệu phải được sắp xếp theo nhiều cách khác nhau. – JayRu

+0

Thực sự khó khăn với row_numbers trên 8 đơn hàng khác nhau, và với phân vùng theo yêu cầu. Ngay cả với các truy vấn phụ mà * có thể * được paralelized, là không họ sẽ. Tùy chọn Paralele là availableas một tùy chọn để thực hiện phân vùng của một hoạt động đơn lẻ, như quét bảng, không phải để tách nhiều truy vấn phụ khác nhau. Tôi sẽ xem xét lại các yêu cầu và xem xét lại nhu cầu cho tất cả các row_numbers ... –

+0

Thật không may, tính toán một trung bình yêu cầu dữ liệu được sắp xếp theo thứ tự. Row_Number chỉ cho bạn biết cách dữ liệu này được sắp xếp cho một trường đã cho. Thx cho sự giúp đỡ cho đến nay ... – JayRu

2

Tôi không chắc chắn rằng nó có thể song song điều này, bởi vì nó cần phải quét không phân vùng (wrt population vs square miles). Họ sẽ xung đột với mỗi đĩa, vì vậy nó phải đưa mọi thứ vào bộ nhớ ít nhất một lần, trước tiên và sau đó nó có thể đủ điều kiện để song song, nếu nó đủ lớn.

Trong mọi trường hợp, sau đây thực hiện đáng kể (40%) nhanh hơn đối với tôi:

;WITH cte AS (
    SELECT 
     StateID 
     ,TimeDimID 
     ,ConstructionStatusID 
     ,NumberOfRows = COUNT(*) OVER (PARTITION BY StateID, TimeDimID, ConstructionStatusID) 
     ,PopulationSizeRowNum = ROW_NUMBER() OVER (PARTITION BY StateID, TimeDimID, ConstructionStatusID ORDER BY PopulationSize) 
     ,SquareMilesRowNum = ROW_NUMBER() OVER (PARTITION BY StateID, TimeDimID, ConstructionStatusID ORDER BY SquareMiles) 
     ,PopulationSize 
     ,SquareMiles 
    FROM TestMedian 
) 
, ctePop AS (
    SELECT MinPopNum = MIN(PopulationSizeRowNum) 
    , MaxPopNum = MAX(PopulationSizeRowNum) 
    , StateID, TimeDimID, ConstructionStatusID 
    , MedianPopulationSize= AVG(PopulationSize) 
    FROM cte T 
    WHERE PopulationSizeRowNum IN((NumberOfRows + 1)/2, (NumberOfRows + 2)/2) 
    GROUP BY StateID, TimeDimID, ConstructionStatusID 
) 
, cteSqM AS (
    SELECT MinSqMNum = MIN(SquareMilesRowNum) 
    , MaxSqMNum = MAX(SquareMilesRowNum) 
    , StateID, TimeDimID, ConstructionStatusID 
    , MedianSquareMiles= AVG(SquareMiles) 
    FROM cte T 
    WHERE SquareMilesRowNum IN((NumberOfRows + 1)/2, (NumberOfRows + 2)/2) 
    GROUP BY StateID, TimeDimID, ConstructionStatusID 
) 
SELECT s.StateID, s.TimeDimID, s.ConstructionStatusID 
, MinPopNum, MaxPopNum, MedianPopulationSize 
, MinSqMNum, MaxSqMNum, MedianSquareMiles 
FROM ctePop p 
JOIN cteSqM s ON s.StateID = p.StateID 
    AND s.TimeDimID = p.TimeDimID 
    AND s.ConstructionStatusID = p.ConstructionStatusID 

Ngoài ra, các loại bản thân nên được song song một khi họ nhận được đủ lớn. Bạn sẽ cần các hàng thử nghiệm ít nhất 100.000 trước khi điều đó có thể xảy ra.


OK, vâng, tôi nhận được xử lý song song sau khi tôi tải nó lên đủ với tuyên bố này:

INSERT INTO TestMedian 
SELECT abs(id)%3,abs(id)%2,abs(id)%5, abs(id), colid * 10000 
    From master.sys.syscolumns, (select top 10 * from master.dbo.spt_values)a 
+0

Thx. Tôi đang thử nghiệm phương pháp này trên tập dữ liệu thực tế của tôi ngay bây giờ để xem liệu số hàng có song song hay không. Trên một tập con nhỏ, nó trông đầy hứa hẹn. – JayRu

1

Một số suy nghĩ bên: Nếu bạn cần dữ liệu này thường xuyên và/hoặc một cách nhanh chóng, và các dữ liệu cơ bản bộ không thay đổi thường xuyên (với các giá trị hợp lý cao "thường xuyên"), bạn có thể tính toán trước bất kỳ giá trị nào trong số này và lưu trữ chúng trong một số dạng bảng tổng hợp trước không?

(Yep, đây là tính năng chuẩn hóa, nhưng nếu bạn cần hiệu suất hơn tất cả những thứ khác, nó đáng xem xét.)

+1

Tôi muốn nói "không chuẩn hóa" ở đó. Thật thà. –

+0

Tôi tin bạn :). Thật không may, tôi không thấy một bước trước khi tập hợp ở đây, mặc dù. Trong ví dụ này, kích thước dân số được trải rộng trên một tập hợp các thứ nguyên. Đối với mỗi bộ kích thước, tôi cần phải tìm giá trị trung bình của kích thước quần thể. Sự kết hợp trước duy nhất mà tôi có thể nghĩ đến là thay thế các tham số riêng lẻ bằng một mã định danh để phân vùng, nhóm và tham gia được thực hiện trên ít cột hơn (có thể thực sự đáng giá). – JayRu

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