查询在Oracle SQL Developer中快速运行,但在SSRS 2008 R2中运行速度很慢
就是这么简单: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_inputs
和no_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应该能够在名为v $ session的视图中识别会话,并且列EVENT和WAIT_CLASS应该指示Oracle端发生了什么。
他还能够识别SQL(来自v $ session的SQL_ID),并在SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(sql_id))中使用它来确定计划。
如果这是一个开发/测试实例,看看他是否会授予您自己的权限,如果他(或她)很忙。
我知道这是旧的,但我们有一个类似的问题,必须将nsl_sort设置为二进制而不是binary_ci。人们可以尝试将会话设置为二进制:alter session set nls_sort = binary
是您的统计信息是最新的吗? – 2011-01-25 04:17:08
不,如我所说供应商不使用统计数据。他们基于规则为每个查询在这个DB有古老的起源。 – ErikE 2011-01-25 05:04:54