sql-server - 查询在 Oracle SQL Developer 中运行很快,但在 SSRS 2008 R2 中运行缓慢

标签 sql-server oracle ssrs-2008 oracle11g execution-time

就是这么简单:在连接到 Oracle 11g 的 SQL Developer 中运行只需几秒钟的查询在 SSRS 2008 R2 中需要 15-25 分钟。我还没有尝试过其他版本的 SSRS。到目前为止,我正在从 VS 2008 执行所有报告。

我正在使用 OLE DB 提供程序“OraOLEDB.Oracle.1”,过去它似乎比使用 Oracle 提供程序给了我更好的结果。

以下是我目前能够确定的:

• 延迟发生在数据集执行阶段,与结果集或渲染时间无关。 (通过直接从我插入的表中选择相同的行集来证明。)

• SSRS 本身没有挂断。它真正在等待 Oracle,这是延迟所在(通过从 Oracle 端终止 DB session 来证明,这导致 SSRS 中关于 session 被终止的提示错误)。

• 我曾尝试使用 :Parameter 形式的参数进行直接查询。我的查询的早期版本更简单,可用于直接查询,但似乎超过了一定的复杂性,查询将永远从 SSRS 开始。

• 然后我切换到执行一个 SP,将查询结果插入到一个表或全局临时表中。这帮助了一段时间,让我比直接查询更远,但同样,几乎似乎增加的查询复杂性或长度最终也破坏了这种方法。注意:运行填充表的 SP 是有效的,因为在 DataSource 选项中选中“使用单个事务”选项后,DataSet 将按照它们在 rdl 文件中的出现顺序运行。只要满足所有参数,不返回字段的数据集仍会运行。

• 我刚刚尝试了一个返回表的函数,但仍然没有任何改进,即使在 SQL Developer 中使用文字参数直接调用在 1-5 秒内返回。

• 有问题的数据库没有统计数据。它是供应商创建的产品的一部分,我们没有时间或管理层支持创建/更新统计数据。我使用 DYNAMIC_SAMPLING 提示来动态计算统计数据并获得了更好的执行计划:如果没有统计数据,基于成本的优化器使用 LOOP 连接而不是 HASH 连接的效果很差,导致类似的多分钟执行时间。因此,我输入了查询提示以强制连接顺序并使其使用战略性哈希连接,从而将执行时间缩短到几秒钟。我没有回去尝试使用这些执行提示在 SSRS 中直接查询。

• 我从我们的 Oracle DBA 那里得到了一些帮助,他设置了一个跟踪(或任何 Oracle 等价物),他能够看到正在运行的东西,但到目前为止他还没有发现任何有用的东西。不幸的是,他的时间有限,我们无法真正深入了解服务器端正在执行的内容。我没有快速完成此操作的经验,也没有时间研究如何自己完成此操作。关于如何确定正在发生的事情的建议将不胜感激。

我唯一的假设是:

• 查询以某种方式获得了错误的执行计划。例如,当有数以万计的“左”或外循环行而不是几百行时,不正确地使用 LOOP 连接而不是 HASH 连接。

• SSRS 可能将参数作为 nvarchar(4000) 或其他东西而不是合理的东西提交,并且由于 Oracle SP 和函数参数没有长度规范但从查询调用中获取它们的执行长度,因此一些过程,例如参数嗅探是像上一点一样弄乱执行计划。

• SSRS/提供者以某种方式重写了查询。我正在使用多值参数,但不是原样:参数作为表达式 Join(Parameters!MultiValuedParameter.Value, ",") 提交,因此不需要任何重写。只是一个简单的绑定(bind)和提交。我不明白在 SP 和函数调用中这怎么可能是真的,但是天哪,还能是什么?

我意识到这是一个非常复杂和冗长的查询,但它正是我所需要的。它会在 1-5 秒内运行,具体取决于请求的数据量。复杂性的一些原因是:

  • 正确处理逗号分隔的成本中心列表参数
  • 允许每周分割是可选的,如果包括在内,即使没有数据,也要确保显示一个月中的所有周。
  • 适当时显示“无发票”。
  • 允许可变数量的汇总月。
  • 有一个可选的 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;
    

    更新

    即使在学习了所有关于 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;
    

    这是一段漫长而痛苦的旅程,但如果我学到了一件事,那就是在没有正确更新统计信息的数据库中工作(即使供应商不关心,我也会考虑让我们的 DBA 添加)关于他们)对于想要在合理的时间内完成任务的人来说可能是一场真正的灾难。

    最佳答案

    发布查询可能会有所帮助。

    您的 DBA 应该能够在名为 v$session 的 View 中识别 session ,并且列 EVENT 和 WAIT_CLASS 应该指示 Oracle 端发生的情况。

    他还能够识别 SQL(来自 v$session 的 SQL_ID)并在 SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(sql_id)) 中使用它来确定计划。

    如果是开发/测试实例,如果他(或她)很忙,看看他是否会授予您自己执行此操作的权限。

    关于sql-server - 查询在 Oracle SQL Developer 中运行很快,但在 SSRS 2008 R2 中运行缓慢,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4788987/

    相关文章:

    ssrs-2008 - 错误 : Subreport could not be shown. 子报告在预览版中有效 - 但在部署时无效

    sql - 根据sql中的日期过滤数据

    php - 带有 MySQL 数据库的 PHP Yii 框架上的应用程序是否可以处理 20,000 名员工的 ERP 解决方案?

    java - Hibernate 一对一映射。仅插入外键而不是整个新行

    java - 如何将时间戳字段发送到 Oracle 存储过程。尽管数据库配置来自 Java?

    sql - oracle sql查询时间戳落在两个时间戳之间的记录

    reporting-services - SSRS Datepicker 控件未在 Chrome 和 safari 中显示

    sql-server - 如何获取本地计算机上所有 MS SQL Server 实例的列表?

    sql-server - 产品的 SSAS 维度显示错误的价格格式

    sql-server - BC30201 错误 SSRS