2011-01-25 172 views
11

就是這麼簡單:SQL Server 2008中連接到Oracle 11g的SQL Developer中運行的查詢在SSRS 2008 R2中需要15-25分鐘。我還沒有嘗試其他版本的SSRS。到目前爲止,我正在執行VS 2008的所有報告執行。查詢在Oracle SQL Developer中快速運行,但在SSRS 2008 R2中運行速度很慢

我使用的OLE DB提供程序「OraOLEDB.Oracle.1」在過去似乎給我比使用Oracle提供程序更好的結果。

這裏是我已經能夠到目前爲止確定:

•延遲是在數據集執行階段,並沒有任何關係,結果集或渲染時間。 (直接從我把它插入到表中選擇相同的行集證明。)

•SSRS本身不是掛了。它確實在等待Oracle,這是延遲的地方(通過終止來自Oracle端的數據庫會話證明,這導致SSRS中關於會話被終止的提示錯誤)。

•我已經試過帶參數的直接查詢形式:參數。非常早期版本的查詢更簡單,可以直接查詢,但似乎已經過了一定的複雜性,查詢將從SSRS開始持續。

•然後我切換到執行SP,將查詢結果插入到表或全局臨時表中。這有助於一段時間,讓我遠離直接查詢,但同樣,查詢複雜度或長度的增加似乎最終也破壞了這種方法。注意:運行一個表格填充SP是可行的,因爲通過在DataSource選項中選中選項「use single transaction」,DataSets會按照它們在rdl文件中出現的順序運行。只要所有參數都滿足,仍然運行不返回字段的數據集。

•我只是想一個表返回函數,這仍然取得了沒有起色,即使在1-5秒SQL Developer的回報文字參數直接調用。

•有問題的數據庫沒有統計信息。它是由供應商創建的產品的一部分,我們沒有時間或管理層的支持來創建/更新統計信息。我使用DYNAMIC_SAMPLING提示來動態計算統計數據,並獲得了更好的執行計劃:沒有統計數據,基於成本的優化器很少使用LOOP連接而不是HASH連接,從而導致類似的執行時間很短。因此,我放入查詢提示來強制連接順序,並使其使用戰略性散列連接,從而將執行時間縮短到僅僅幾秒。我沒有回去嘗試使用這些執行提示在SSRS中直接查詢。

•我從我們的Oracle DBA一些幫助誰建立了跟蹤(或任何甲骨文當量),他能看到的東西正在運行,但他一直沒有找到什麼有用的東西爲止。不幸的是,他的時間有限,我們無法真正瞭解服務器端正在執行什麼。我沒有這麼快的經驗,或者沒有時間研究如何自己做這個。值得讚賞的是如何確定發生了什麼的建議。

我唯一的假設是:

•查詢以某種方式得到一個糟糕的執行計劃。例如,當存在數以萬計的「左」或外環行而不是僅僅幾百個時,不恰當地使用LOOP連接而不是HASH連接。

•SSRS可能會提交參數爲nvarchar(4000)或其他合理的東西,並且由於函數參數沒有長度規範,但從查詢調用中獲得其執行長度,因此某些進程因爲參數嗅探正在搞亂執行計劃,就像前一點一樣。

•查詢以某種方式被SSRS /提供者重寫。我正在使用多值參數,但不是這樣:參數被提交爲表達式Join(Parameters!MultiValuedParameter.Value,「,」),因此它不需要任何重寫。只需一個簡單的綁定和提交。我不明白SP和函數調用可能如此,但是天哪,它還有什麼可能呢?

我意識到這是一個非常複雜和冗長的查詢,但它確實正是我所需要的。它在1-5秒內運行,具體取決於請求的數據量。有些原因的複雜性是:

  • 妥善處理逗號分隔的成本中心列表參數
  • 允許每週擊穿是可選的,如果包括,確保在一個月內衆所周之顯示即使沒有他們的數據。
  • 適當時顯示「No Invoices」。
  • 允許可變數量的彙總月份。
  • 可選YTD總數。
  • 包括以前/歷史比較數據意味着我不能簡單地使用這個個月的供應商,我必須顯示將在任何歷史專欄中的所有供應商。

無論如何,所以這裏是查詢,SP版本(雖然我不認爲這會有很大的幫助)。

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; 

UPDATE

即使是學習所有有關Oracle執行計劃和提示(翻譯我的SQL Server知識)後,我仍然無法得到查詢到在SSRS快速運行,直到我做到了運行在兩個步驟中,首先將實際表格結果放入GLOBAL TEMPORARY TABLE,然後再從中提取數據。 DYNAMIC_SAMPLING給了我一個很好的執行計劃,然後我使用連接和訪問提示進行復制。下面是最後的SP(它不能是一個函數,因爲在Oracle中,當在SELECT語句中調用該函數時,不能在函數中執行DML):

有時我發誓它忽略了我的聯接提示,例如如swap_join_inputsno_swap_join_inputs,但是從我的閱讀中可以明顯地看出,甲骨文在實際上無法使用或者做錯了某些事情時只會忽略提示。幸運的是,這些表格適當地交換了(如USE_NL(CC)那樣,它可靠地將CC表格作爲交換的左側輸入,儘管它最後加入)。

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; 

這是一個漫長,痛苦的騎,但如果有一件事我已經知道它是在一個數據庫中工作不正常更新的統計數據(這我要去尋找到讓我們的數據庫管理員甚至添加儘管供應商不關心他們)對於想要在合理的時間內完成任務的人來說可能是一場真正的災難。

+0

是您的統計信息是最新的嗎? – 2011-01-25 04:17:08

+0

不,如我所說供應商不使用統計數據。他們基於規則爲每個查詢在這個DB有古老的起源。 – ErikE 2011-01-25 05:04:54

回答

1

發佈查詢可能會有幫助。

您的DBA應該能夠在名爲v $ session的視圖中識別會話,並且列EVENT和WAIT_CLASS應該指示Oracle端發生了什麼。

他還能夠識別SQL(來自v $ session的SQL_ID),並在SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(sql_id))中使用它來確定計劃。

如果這是一個開發/測試實例,看看他是否會授予您自己的權限,如果他(或她)很忙。

1

我知道這是舊的,但我們有一個類似的問題,必須將nsl_sort設置爲二進制而不是binary_ci。人們可以嘗試將會話設置爲二進制:alter session set nls_sort = binary

相關問題