【问题标题】:Oracle - Function vs Procedure PerformanceOracle - 函数与过程性能
【发布时间】: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


【解决方案1】:

这个问题已经被问过(如果不是很多次)。您可以找到更详细的答案here,但要简要回答您的问题:

不,它不会影响性能。函数和过程之间的唯一区别是它们的工作方式。使用最适合您情况的一种。

【讨论】:

    猜你喜欢
    • 2014-10-14
    • 2018-08-27
    • 1970-01-01
    • 2015-03-06
    • 2018-02-27
    • 2013-07-15
    • 2013-09-26
    • 1970-01-01
    • 2018-09-11
    相关资源
    最近更新 更多