2011-01-25 31 views
11

Thật đơn giản: truy vấn chạy chỉ trong vài giây trong SQL Developer kết nối với Oracle 11g mất 15-25 phút trong SSRS 2008 R2. Tôi đã không thử các phiên bản khác của SSRS. Cho đến nay tôi đang thực hiện tất cả việc thực hiện báo cáo từ VS 2008.Truy vấn chạy nhanh trong Oracle SQL Developer, nhưng chậm trong SSRS 2008 R2

Tôi đang sử dụng Nhà cung cấp OLE DB "OraOLEDB.Oracle.1" mà trước đây dường như cho tôi kết quả tốt hơn so với sử dụng nhà cung cấp Oracle.

Đây là những gì tôi đã có thể để xác định cho đến nay:

• Việc chậm trễ là trong giai đoạn thực hiện DataSet và không có gì để làm với các kết quả thiết lập hoặc thời gian render. (Chứng minh bằng cách chọn cùng một rowset trực tiếp từ một bảng mà tôi đã chèn nó vào.)

• Bản thân SSRS không bị treo. Nó thực sự chờ đợi Oracle là nơi trì hoãn (được chứng minh bằng cách chấm dứt phiên DB từ phía Oracle, dẫn đến một lỗi nhắc nhở trong SSRS về phiên bị giết).

• Tôi đã thử truy vấn trực tiếp với các tham số trong biểu mẫu: Tham số. Các phiên bản đầu tiên của truy vấn của tôi đơn giản hơn rất nhiều cho việc truy vấn trực tiếp, nhưng nó dường như quá phức tạp nhất định, truy vấn sẽ bắt đầu lấy từ SSRS mãi mãi.

• Sau đó tôi chuyển sang thực thi một SP chèn các kết quả truy vấn vào bảng hoặc bảng tạm thời toàn cầu. Điều này đã giúp trong một thời gian ngắn, làm cho tôi xa hơn so với truy vấn trực tiếp, nhưng một lần nữa, nó gần như có vẻ như tăng độ phức tạp truy vấn hoặc chiều dài cuối cùng đã phá vỡ phương pháp này, quá. Lưu ý: việc chạy một tệp bảng SP hoạt động vì tùy chọn "sử dụng giao dịch đơn lẻ" được chọn trong tùy chọn DataSource, DataSets sau đó được chạy theo thứ tự xuất hiện của chúng trong tệp rdl. Các DataSets trả về không có Fields nào vẫn chạy, miễn là tất cả các tham số của chúng đều được thỏa mãn.

• Tôi vừa thử chức năng trả lại bảng và điều này vẫn không cải thiện, mặc dù các cuộc gọi trực tiếp với tham số theo nghĩa đen trong SQL Developer sẽ trở lại sau 1-5 giây.

• Cơ sở dữ liệu được đề cập không có số liệu thống kê. Nó là một phần của một sản phẩm được tạo ra bởi một nhà cung cấp và chúng tôi đã không có thời gian hoặc quản lý mua để tạo/cập nhật số liệu thống kê. Tôi đã chơi với gợi ý DYNAMIC_SAMPLING để tính toán số liệu thống kê và có kế hoạch thực hiện tốt hơn: không có thống kê mà trình tối ưu hóa dựa trên chi phí đã sử dụng LOOP kém thay vì tham gia HASH, gây ra nhiều lần thực thi tương tự. Vì vậy, tôi đưa ra gợi ý truy vấn để buộc tham gia thứ tự và cũng để gây ra nó để sử dụng tham gia băm chiến lược, đưa thời gian thực hiện trở lại chỉ vài giây. Tôi đã không quay trở lại và thử truy vấn trực tiếp trong SSRS bằng cách sử dụng các gợi ý thực hiện.

• Tôi có một số trợ giúp từ Oracle DBA của chúng tôi, người đã thiết lập một dấu vết (hoặc bất kỳ thứ gì tương đương với Oracle) và anh ta có thể thấy mọi thứ đang chạy, nhưng anh ta không tìm thấy gì hữu ích cho đến nay. Thật không may thời gian của mình là hạn chế và chúng tôi đã không thể thực sự khai thác để tìm hiểu những gì đang được thực hiện phía máy chủ. Tôi không có kinh nghiệm để làm điều này một cách nhanh chóng hoặc thời gian để nghiên cứu về cách làm điều này bản thân mình. Gợi ý về những gì cần làm để xác định những gì đang xảy ra sẽ được đánh giá cao.

giả thuyết duy nhất của tôi là:

• Các truy vấn được bằng cách nào đó nhận được một kế hoạch thực hiện xấu. Ví dụ: sử dụng LOOP không đúng cách thay vì tham gia HASH khi có hàng chục nghìn hàng "bên trái" hoặc hàng bên ngoài thay vì chỉ vài trăm.

• SSRS có thể gửi các tham số như nvarchar (4000) hoặc thứ gì đó thay vì hợp lý, và như Oracle SP & thông số chức năng không có thông số kỹ thuật về độ dài nhưng có độ dài thực hiện từ cuộc gọi truy vấn. như tham số sniffing là rối tung lên kế hoạch thực hiện như trong điểm trước đó.

• Truy vấn bằng cách nào đó được viết lại bởi SSRS/nhà cung cấp. Tôi đang sử dụng một tham số đa giá trị, nhưng không phải là: tham số đang được gửi dưới dạng tham gia biểu thức (Tham số! MultiValuedParameter.Value, ",") nên không cần viết lại. Chỉ cần một ràng buộc đơn giản và gửi. Tôi không thấy làm thế nào điều này có thể đúng trong SP và các chức năng gọi, nhưng gosh, những gì khác nó có thể được?

Tôi nhận ra đó là một truy vấn rất phức tạp và dài dòng, nhưng nó thực hiện chính xác những gì tôi cần. Nó chạy trong 1-5 giây tùy thuộc vào bao nhiêu dữ liệu được yêu cầu. Một số trong những lý do cho sự phức tạp là:

  • đúng xử lý các chi phí danh sách trung tâm tham số bằng dấu phẩy
  • Cho phép phân tích hàng tuần là không bắt buộc và nếu có, đảm bảo tất cả các tuần trong một tháng được hiển thị ngay cả khi không có dữ liệu cho họ.
  • Hiển thị "Không có hóa đơn" khi thích hợp.
  • Cho phép số tháng tóm tắt thay đổi.
  • Có tổng số YTD tùy chọn.
  • Bao gồm dữ liệu so sánh trước đây/lịch sử có nghĩa là tôi không thể đơn giản sử dụng nhà cung cấp của tháng này, tôi phải hiển thị tất cả các nhà cung cấp sẽ ở trong bất kỳ cột lịch sử nào.

Dù sao thì đây là truy vấn, phiên bản SP (mặc dù tôi không nghĩ rằng nó sẽ giúp ích nhiều).

create or replace 
PROCEDURE VendorInvoiceSummary (
    FromDate IN date, 
    ToDate IN date, 
    CostCenterList IN varchar2, 
    IncludeWeekly IN varchar2, 
    ComparisonMonths IN number, 
    IncludeYTD IN varchar2 
) 
AS 
BEGIN 

INSERT INTO InvoiceSummary (Mo, CostCenter, Vendor, VendorName, Section, TimeUnit, Amt) 
SELECT 
    Mo, 
    CostCenter, 
    Vendor, 
    VendorName, 
    Section, 
    TimeUnit, 
    Amt 
FROM (
    WITH CostCenters AS (
     SELECT Substr(REGEXP_SUBSTR(CostCenterList, '[^,]+', 1, LEVEL) || '    ', 1, 15) CostCenter 
     FROM DUAL 
     CONNECT BY LEVEL <= Length(CostCenterList) - Length(Replace(CostCenterList, ',', '')) + 1 
    ), Invoices AS (
     SELECT /*+ORDERED USE_HASH(D)*/ 
     TRUNC(I.Invoice_Dte, 'YYYY') Yr, 
     TRUNC(I.Invoice_Dte, 'MM') Mo, 
     D.Dis_Acct_Unit CostCenter, 
     I.Vendor, 
     V.Vendor_VName, 
     CASE 
      WHEN I.Invoice_Dte >= FromDate AND I.Invoice_Dte < ToDate 
      THEN (TRUNC(I.Invoice_Dte, 'W') - TRUNC(I.Invoice_Dte, 'MM'))/7 + 1 
      ELSE 0 
     END WkNum, 
     Sum(D.To_Base_Amt) To_Base_Amt 
     FROM 
     ICCompany C 
     INNER JOIN APInvoice I 
      ON C.Company = I.Company 
     INNER JOIN APDistrib D 
      ON C.Company = D.Company 
      AND I.Invoice = D.Invoice 
      AND I.Vendor = D.Vendor 
      AND I.Suffix = D.Suffix 
     INNER JOIN CostCenters CC 
      ON D.Dis_Acct_Unit = CC.CostCenter 
     INNER JOIN APVenMast V ON I.Vendor = V.Vendor 
     WHERE 
     D.Cancel_Seq = 0 
     AND I.Cancel_Seq = 0 
     AND I.Invoice_Dte >= Least(ADD_MONTHS(FromDate, -ComparisonMonths), TRUNC(FromDate, 'YYYY')) 
     AND I.Invoice_Dte < ToDate 
     AND V.Vendor_Group = '1 ' -- index help 
     GROUP BY 
     TRUNC(I.Invoice_Dte, 'YYYY'), 
     TRUNC(I.Invoice_Dte, 'MM'), 
     D.Dis_Acct_Unit, 
     I.Vendor, 
     V.Vendor_VName, 
     CASE 
      WHEN I.Invoice_Dte >= FromDate AND I.Invoice_Dte < ToDate 
      THEN (TRUNC(I.Invoice_Dte, 'W') - TRUNC(I.Invoice_Dte, 'MM'))/7 + 1 
      ELSE 0 
     END 
    ), Months AS (
     SELECT ADD_MONTHS(Least(ADD_MONTHS(FromDate, -ComparisonMonths), TRUNC(FromDate, 'YYYY')), LEVEL - 1) Mo 
     FROM DUAL 
     CONNECT BY LEVEL <= MONTHS_BETWEEN(ToDate, Least(ADD_MONTHS(FromDate, -ComparisonMonths), TRUNC(FromDate, 'YYYY'))) 
    ), Sections AS (
     SELECT 1 Section, 1 StartUnit, 5 EndUnit FROM DUAL 
     UNION ALL SELECT 2, 0, ComparisonMonths FROM DUAL 
     UNION ALL SELECT 3, 1, 1 FROM DUAL WHERE IncludeYTD = 'Y' 
    ), Vals AS (
     SELECT LEVEL - 1 TimeUnit 
     FROM DUAL 
     CONNECT BY LEVEL <= (SELECT Max(EndUnit) FROM Sections) + 1 
    ), TimeUnits AS (
     SELECT S.Section, V.TimeUnit 
     FROM 
     Sections S 
     INNER JOIN Vals V 
      ON V.TimeUnit BETWEEN S.StartUnit AND S.EndUnit 
    ), Names AS (
     SELECT DISTINCT 
     M.Mo, 
     Coalesce(I.Vendor, '0') Vendor, 
     Coalesce(I.Vendor_VName, 'No Paid Invoices') Vendor_VName, 
     Coalesce(I.CostCenter, ' ') CostCenter 
     FROM 
     Months M 
     LEFT JOIN Invoices I 
      ON Least(ADD_MONTHS(M.Mo, -ComparisonMonths), TRUNC(M.Mo, 'YYYY')) < I.Mo 
      AND M.Mo >= I.Mo 
     WHERE 
     M.Mo >= FromDate 
     AND M.Mo < ToDate 
    ) 
    SELECT 
     N.Mo, 
     N.CostCenter, 
     N.Vendor, 
     N.Vendor_VName VendorName, 
     T.Section, 
     T.TimeUnit, 
     Sum(I.To_Base_Amt) Amt 
    FROM 
     Names N 
     CROSS JOIN TimeUnits T 
     LEFT JOIN Invoices I 
     ON N.CostCenter = I.CostCenter 
     AND N.Vendor = I.Vendor 
     AND (
      (
       T.Section = 1 -- Weeks for current month 
       AND N.Mo = I.Mo 
       AND T.TimeUnit = I.WkNum 
      ) OR (
       T.Section = 2 -- Summary months 
       AND ADD_MONTHS(N.Mo, -T.TimeUnit) = I.Mo 
      ) OR (
       T.Section = 3 -- YTD 
       AND I.Mo BETWEEN TRUNC(N.Mo, 'YYYY') AND N.Mo 
      ) 
     ) 
    WHERE 
     N.Mo >= FromDate 
     AND N.Mo < ToDate 
     AND NOT (-- Only 4 weeks when a month is less than 28 days long 
     T.Section = 2 
     AND T.TimeUnit = 5 
     AND TRUNC(N.Mo + 28, 'MM') <> N.Mo 
     AND I.CostCenter IS NULL 
    ) AND (
     T.Section <> 1 
     OR IncludeWeekly = 'Y' 
    ) 
    GROUP BY 
     N.Mo, 
     N.CostCenter, 
     N.Vendor, 
     N.Vendor_VName, 
     T.Section, 
     T.TimeUnit 
) X; 
COMMIT; 
END; 

CẬP NHẬT

Ngay cả sau khi biết tất cả về kế hoạch thực hiện Oracle và gợi ý (dịch SQL Server kiến ​​thức của tôi), tôi vẫn không thể có được truy vấn để chạy một cách nhanh chóng trong SSRS cho đến khi tôi đã làm cho nó chạy trong hai bước, trước tiên hãy đặt kết quả bảng thực vào một số GLOBAL TEMPORARY TABLE và sau đó thứ hai để trích xuất dữ liệu từ đó. DYNAMIC_SAMPLING đã cho tôi một kế hoạch thực hiện tốt, sau đó tôi đã sao chép bằng cách sử dụng các gợi ý tham gia và truy cập. Đây là SP cuối cùng (nó không thể là một hàm vì trong Oracle bạn không thể làm DML trong một hàm khi hàm đó được gọi bên trong câu lệnh SELECT):

Thỉnh thoảng tôi thề là bỏ qua các gợi ý tham gia của tôi như swap_join_inputsno_swap_join_inputs nhưng từ đọc của tôi dường như Oracle chỉ bỏ qua gợi ý khi chúng thực sự không thể được sử dụng hoặc bạn đang làm điều gì sai. May mắn thay, các bảng được hoán đổi một cách thích hợp (như trong trường hợp của USE_NL(CC) nó đáng tin cậy đặt bảng CC là đầu vào được hoán đổi, trái, mặc dù nó được nối lần cuối).

CREATE OR REPLACE 
PROCEDURE VendorInvoicesSummary (
    FromDate IN date, 
    ToDate IN date, 
    CostCenterList IN varchar2, 
    IncludeWeekly IN varchar2, 
    ComparisonMonths IN number, 
    IncludeYTD IN varchar2 
) 
AS 
BEGIN 

INSERT INTO InvoiceTemp (Yr, Mo, CostCenter, Vendor, WkNum, Amt) -- A global temporary table 
SELECT /*+LEADING(C I D CC) USE_HASH(I D) USE_NL(CC)*/ 
    TRUNC(I.Invoice_Dte, 'YYYY') Yr, 
    TRUNC(I.Invoice_Dte, 'MM') Mo, 
    D.Dis_Acct_Unit CostCenter, 
    I.Vendor, 
    CASE 
     WHEN I.Invoice_Dte >= FromDate AND I.Invoice_Dte < ToDate 
     THEN (TRUNC(I.Invoice_Dte, 'W') - TRUNC(I.Invoice_Dte, 'MM'))/7 + 1 
     ELSE 0 
    END WkNum, 
    Sum(D.To_Base_Amt) To_Base_Amt 
FROM 
    ICCompany C 
    INNER JOIN APInvoice I 
     ON C.Company = I.Company 
    INNER JOIN APDistrib D 
     ON C.Company = D.Company 
     AND I.Invoice = D.Invoice 
     AND I.Vendor = D.Vendor 
     AND I.Suffix = D.Suffix 
    INNER JOIN (
     SELECT Substr(REGEXP_SUBSTR(CostCenterList, '[^,]+', 1, LEVEL) || '    ', 1, 15) CostCenter 
     FROM DUAL 
     CONNECT BY LEVEL <= Length(CostCenterList) - Length(Replace(CostCenterList, ',', '')) + 1 
    ) CC ON D.Dis_Acct_Unit = CC.CostCenter 
WHERE 
    D.Cancel_Seq = 0 
    AND I.Cancel_Seq = 0 
    AND I.Invoice_Dte >= Least(ADD_MONTHS(FromDate, -ComparisonMonths), TRUNC(FromDate, 'YYYY')) 
    AND I.Invoice_Dte < ToDate 
GROUP BY 
    TRUNC(I.Invoice_Dte, 'YYYY'), 
    TRUNC(I.Invoice_Dte, 'MM'), 
    D.Dis_Acct_Unit, 
    I.Vendor, 
    CASE 
     WHEN I.Invoice_Dte >= FromDate AND I.Invoice_Dte < ToDate 
     THEN (TRUNC(I.Invoice_Dte, 'W') - TRUNC(I.Invoice_Dte, 'MM'))/7 + 1 
     ELSE 0 
    END; 

INSERT INTO InvoiceSummary (Mo, CostCenter, Vendor, VendorName, Section, TimeUnit, Amt) 
SELECT 
    Mo, 
    CostCenter, 
    Vendor, 
    VendorName, 
    Section, 
    TimeUnit, 
    Amt 
FROM (
    WITH Months AS (
     SELECT ADD_MONTHS(Least(ADD_MONTHS(FromDate, -ComparisonMonths), TRUNC(FromDate, 'YYYY')), LEVEL - 1) Mo 
     FROM DUAL 
     CONNECT BY LEVEL <= MONTHS_BETWEEN(ToDate, Least(ADD_MONTHS(FromDate, -ComparisonMonths), TRUNC(FromDate, 'YYYY'))) 
    ), Sections AS (
     SELECT 1 Section, 1 StartUnit, 5 EndUnit FROM DUAL 
     UNION ALL SELECT 2, 0, ComparisonMonths FROM DUAL 
     UNION ALL SELECT 3, 1, 1 FROM DUAL WHERE IncludeYTD = 'Y' 
    ), Vals AS (
     SELECT LEVEL - 1 TimeUnit 
     FROM DUAL 
     CONNECT BY LEVEL <= (SELECT Max(EndUnit) FROM Sections) + 1 
    ), TimeUnits AS (
     SELECT S.Section, V.TimeUnit 
     FROM 
     Sections S 
     INNER JOIN Vals V 
      ON V.TimeUnit BETWEEN S.StartUnit AND S.EndUnit 
    ), Names AS (
     SELECT DISTINCT 
     M.Mo, 
     Coalesce(I.Vendor, '0') Vendor, 
     Coalesce(I.CostCenter, ' ') CostCenter 
     FROM 
     Months M 
     LEFT JOIN InvoiceTemp I 
      ON Least(ADD_MONTHS(M.Mo, -ComparisonMonths), TRUNC(M.Mo, 'YYYY')) <= I.Mo 
      AND I.Mo <= M.Mo 
     WHERE 
     M.Mo >= FromDate 
     AND M.Mo < ToDate 
    ) 
    SELECT 
     N.Mo, 
     N.CostCenter, 
     N.Vendor, 
     Coalesce(V.Vendor_VName, 'No Paid Invoices') VendorName, 
     T.Section, 
     T.TimeUnit, 
     Sum(I.Amt) Amt 
    FROM 
     Names N 
     INNER JOIN APVenMast V ON N.Vendor = V.Vendor 
     CROSS JOIN TimeUnits T 
     LEFT JOIN InvoiceTemp I 
     ON N.CostCenter = I.CostCenter 
     AND N.Vendor = I.Vendor 
     AND (
      (
       T.Section = 1 -- Weeks for current month 
       AND N.Mo = I.Mo 
       AND T.TimeUnit = I.WkNum 
      ) OR (
       T.Section = 2 -- Summary months 
       AND ADD_MONTHS(N.Mo, -T.TimeUnit) = I.Mo 
      ) OR (
       T.Section = 3 -- YTD 
       AND I.Mo BETWEEN TRUNC(N.Mo, 'YYYY') AND N.Mo 
      ) 
     ) 
    WHERE 
     N.Mo >= FromDate 
     AND N.Mo < ToDate 
     AND V.Vendor_Group = '1 ' 
     AND NOT (-- Only 4 weeks when a month is less than 28 days long 
     T.Section = 2 
     AND T.TimeUnit = 5 
     AND TRUNC(N.Mo + 28, 'MM') <> N.Mo 
     AND I.CostCenter IS NULL 
    ) AND (
     T.Section <> 1 
     OR IncludeWeekly = 'Y' 
    ) 
    GROUP BY 
     N.Mo, 
     N.CostCenter, 
     N.Vendor, 
     V.Vendor_VName, 
     T.Section, 
     T.TimeUnit 
) X; 

COMMIT; 
END; 

này đã được một chặng đường dài, đau đớn đi xe, nhưng nếu có một điều tôi đã học được nó mà làm việc trong một cơ sở dữ liệu mà không thống kê được cập nhật đúng (mà tôi sẽ xem xét nhận được DBA của chúng tôi để thêm thậm chí mặc dù nhà cung cấp không quan tâm đến họ) có thể là một thảm họa thực sự cho những người muốn hoàn thành công việc trong một khoảng thời gian hợp lý.

+0

là các thống kê của bạn được cập nhật? –

+0

Không, như tôi đã nói nhà cung cấp không sử dụng số liệu thống kê. Họ làm dựa trên quy tắc cho mọi truy vấn trong DB này có nguồn gốc cổ đại. – ErikE

Trả lời

1

Đăng truy vấn có thể hữu ích.

DBA của bạn sẽ có thể xác định phiên trong chế độ xem có tên là v $ session và các cột EVENT và WAIT_CLASS sẽ đưa ra chỉ báo về những gì đang xảy ra ở cuối Oracle.

Ông cũng có thể xác định SQL (SQL_ID từ phiên v $) và sử dụng nó trong một SELECT * FROM TABLE (DBMS_XPLAN.DISPLAY_CURSOR (sql_id)) để xác định kế hoạch.

Nếu đó là một ví dụ phát triển/kiểm tra, hãy xem liệu anh ấy có cấp cho bạn quyền để tự mình thực hiện điều đó nếu anh ấy (hoặc cô ấy) bận.

+0

Đã thêm truy vấn. Tôi sẽ xem xét các đề xuất khác của bạn. – ErikE

+0

Cảm ơn bạn. Thấy kế hoạch thực hiện (hoàn toàn khác với kế hoạch tôi nhận được từ nhà phát triển SQL) đã chứng minh rằng thực sự là vấn đề. Bây giờ tôi đang ở sâu trong khu rừng để tìm ra cách ép buộc kế hoạch thực hiện mà tôi cần. Thở dài. – ErikE

+0

P.S. Trên thực tế, tôi đã vật lộn và đấu tranh và không bao giờ có thể làm cho nó hoạt động đáng tin cậy cho đến khi tôi đưa kết quả truy cập bảng chính vào một TẠM THỜI HÓA GLOBAL và sau đó tham gia vào đó trong phần còn lại của truy vấn. '/ * + MATERIALIZE * /' dường như cũng không giúp được gì. Một số điều này vẫn còn là một bí ẩn đối với tôi. Một bí ẩn đau đớn, đau đớn. Những giờ tôi đã lãng phí về điều này ... – ErikE

1

Tôi biết điều này là cũ nhưng chúng tôi đã có một vấn đề tương tự và đã phải đặt nsl_sort thành nhị phân thay vì binary_ci. Mọi người có thể thử đặt phiên thành nhị phân: thay đổi tập hợp phiên nls_sort = binary

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