【发布时间】:2021-11-28 13:45:00
【问题描述】:
我正在组合一个包来执行一些操作并返回一个关联的日期范围数组。我一直在使用函数,但我开始怀疑这是否是性能方面的最佳选择,所以我整理了一个测试包来检查它:
CREATE OR REPLACE PACKAGE pk_perf_test IS
TYPE pt_rDateSpan IS RECORD
(
start_date DATE,
end_date DATE
);
TYPE pt_aaDateTable IS TABLE OF pt_rDateSpan INDEX BY PLS_INTEGER;
FUNCTION pf_getDateTable (p_nInputParam INTEGER) RETURN pt_aaDateTable;
PROCEDURE pp_getDateTable (p_nInputParam INTEGER, op_aaDateTable OUT pt_aaDateTable);
PROCEDURE pp_getDateTable_NoCopy (p_nInputParam INTEGER, op_aaDateTable OUT NOCOPY pt_aaDateTable);
END pk_perf_test;
每个函数/过程只需使用输入参数作为绑定变量在集合中运行带有BULK COLLECT 的相同查询。像这样:
CREATE OR REPLACE PACKAGE BODY pk_perf_test IS
FUNCTION pf_getDateTable
(p_nInputParam INTEGER)
RETURN pt_aaDateTable
IS
l_aaToRet pt_aaDateTable;
BEGIN
SELECT mt.start_date, mt.end_date
BULK COLLECT INTO l_aaToRet
FROM my_table mt
INNER JOIN my_other_table mot ON mt.pk_col = mot.fk_col
WHERE mt.pk_col = p_nInputParam;
RETURN l_aaToRet;
END pf_getDateTable;
PROCEDURE pp_getDateTable
(p_nInputParam INTEGER, op_aaDateTable OUT pt_aaDateTable)
IS
BEGIN
SELECT mt.start_date, mt.end_date
BULK COLLECT INTO op_aaDateTable
FROM my_table mt
INNER JOIN my_other_table mot ON mt.pk_col = mot.fk_col
WHERE mt.pk_col = p_nInputParam;
END pp_getDateTable;
PROCEDURE pp_getDateTable_NoCopy
(p_nInputParam INTEGER, op_aaDateTable OUT NOCOPY pt_aaDateTable)
IS
BEGIN
SELECT mt.start_date, mt.end_date
BULK COLLECT INTO op_aaDateTable
FROM my_table mt
INNER JOIN my_other_table mot ON mt.pk_col = mot.fk_col
WHERE mt.pk_col = p_nInputParam;
END pp_getDateTable_NoCopy;
END pk_perf_test;
然后我编写了一个简短的脚本来测试这些以确定性能:
DECLARE
l_tsStartTS TIMESTAMP WITH LOCAL TIME ZONE;
l_iFunctionTotal INTERVAL DAY TO SECOND := INTERVAL '0' SECOND;
l_iProcedureTotal INTERVAL DAY TO SECOND := INTERVAL '0' SECOND;
l_iNoCopyTotal INTERVAL DAY TO SECOND := INTERVAL '0' SECOND;
l_aaDummy pk_perf_test.pt_aaDateTable;
l_nDummy INTEGER := 123;
l_nCaseCount INTEGER := 0;
CURSOR c_ids IS
SELECT mt.pk_col
FROM my_table SAMPLE (10) mt;
BEGIN
--Priming Query Run to make sure plan is cached
SELECT mt.start_date, mt.end_date
BULK COLLECT INTO l_aaDummy
FROM my_table mt
INNER JOIN my_other_table mot ON mt.pk_col = mot.fk_col
WHERE mt.pk_col = l_nDummy;
FOR r IN c_ids LOOP
l_nCaseCount := l_nCaseCount + 1;
l_aaDummy.DELETE;
l_tsStartTS := SYSTIMESTAMP;
l_aaDummy := pk_perf_test.pf_getDateTable(r.pk_col);
l_iFunctionTotal := l_iFunctionTotal + (SYSTIMESTAMP-l_tsStartTS);
l_aaDummy.DELETE;
l_tsStartTS := SYSTIMESTAMP;
pk_perf_test.pp_getDateTable(r.pk_col, l_aaDummy);
l_iProcedureTotal := l_iProcedureTotal + (SYSTIMESTAMP-l_tsStartTS);
l_aaDummy.DELETE;
l_tsStartTS := SYSTIMESTAMP;
pk_perf_test.pp_getDateTable_NoCopy(r.pk_col, l_aaDummy);
l_iNoCopyTotal := l_iNoCopyTotal + (SYSTIMESTAMP-l_tsStartTS);
END LOOP;
dbms_output.put_line('Total Cases: '||l_nCaseCount);
dbms_output.put_line('Function Total: '||l_iFunctionTotal);
dbms_output.put_line('Procedure Total: '||l_iProcedureTotal);
dbms_output.put_line('No Copy Total: '||l_iNoCopyTotal);
END;
/
我收到以下结果:
病例总数:10787 函数总数:+00 00:00:22.857400 程序总计:+00 00:00:04.346413 无副本总计:+00 00:00:01.942333
现在回答我的问题。我明白为什么NOCOPY 比正常程序更快。我不明白的是为什么常规过程比函数快得多?认为这是侥幸,我在多个环境(多次)上运行它并收到了相似的结果(我发布的结果实际上是所有运行的平均值)。谁能解释为什么性能会如此不同?或者指出我测试中的缺陷?
【问题讨论】:
-
我不认为这个过程更快。 Oracle 只是从缓存中读取结果。尝试以不同的顺序执行它们。无论如何,瓶颈应该是 SELECT 语句。
-
@WernfriedDomscheit 你似乎是对的。颠倒顺序得到以下结果。函数总计:+00 00:00:02.192932 过程总计:+00 00:00:07.502305 无复制总计:+00 00:00:22.206270
-
在 SQL 和/或存储过程/函数之间运行性能比较时,在每个之前运行
alter system flush shared pool;,并运行多次迭代。这使得运行开始时没有缓冲结果形成对相同或几乎相同数据的先前访问。注意:在生产环境中不是一个好主意。 -
这可能与 stackoverflow.com/q/25419629/409172 重复 - tldr - 过程和函数之间存在一些细微的性能差异,具体取决于 PL/SQL 优化级别,但差异并不重要。
标签: oracle performance plsql