【问题标题】:Oracle stored procedure taking more time compared to normal query与普通查询相比,Oracle 存储过程需要更多时间
【发布时间】:2020-10-02 08:55:42
【问题描述】:

我有一个如下所示的 Oracle SQL 查询,

select * 
from employees a, department b 
where a.empoyee_id = 10
and a.dept_no = b.dept_no
and a.salary between 10000 and 20000
and a.start_date between date1 and date2 
and a.end_date between date3 and date4

以上查询运行良好,几秒钟内即可获取结果。

但如果将其转换为如下所示的存储过程,

Procedure GETDATA(
   EMP_ID IN NUMBER,
   MIN_SAl IN NUMBER,
   MAX_SAL IN NUMBER,
   MIN_START_DATE IN VARCHAR2,
   MAX_START_DATE IN VARCHAR2, 
   MIN_END_DATE IN VARCHAR2,
   MAX_END_DATE IN VARCHAR2, 
   RESULT OUT dataset
)
IS
BEGIN
open RESULT FOR
   select * from employees a, department b 
   where EMPLOYEE_ID = EMP_ID AND a.dept_no = b.dept_no
    and (MIN_SAl IS NULL OR MAX_SAL IS NULL) OR (a.salary between MIN_SAl and MAX_SAL)
    and (MIN_START_DATE IS NULL OR MAX_START_DATE IS NULL) OR (a.start_date between MIN_START_DATE and MAX_START_DATE)
    and (MIN_END_DATE IS NULL OR MAX_END_DATE IS NULL) OR (a.end_date between MIN_END_DATE and MAX_END_DATE); 
END GETDATA;

exec GETDATA(10, NULL, NULL, NULL, NULL, NULL, NULL, :p)

上面的存储过程需要 10 多秒,但单独运行它会在几秒钟内得到结果。我看到为 where 子句(salary、start_date、end_date)中的所有列添加了索引

我发现日期标准需要更多时间。我用谷歌搜索并将 start_date 和 end_date 数据类型从 VARCHAR 修改为 DATE,但仍然没有运气。为什么在存储过程中花费更多时间,但在几秒钟内独立运行?

【问题讨论】:

  • 查询类似但不一样:请使用stackoverflow.com/questions/34975406/…发布执行计划
  • 要验证问题到底出在哪里,请将此查询作为游标运行,然后遍历游标并查看需要多少时间。同样按照建议粘贴此查询的执行计划。
  • 我相信你有括号错误。不应该是AND (min_x IS NULL OR max_x IS NULL or x BETWEEN min_x AND max_x)吗?

标签: sql oracle stored-procedures plsql


【解决方案1】:

使用 CTAS 测试您的查询。你会发现它几乎需要同样的时间才能完成。当您只选择表格时,几乎所有代码编辑器都会首先提供最先可用的结果。

CREATE TABLE TEST
AS
select * 
from employees a, department b 
where a.empoyee_id = 10
and a.dept_no = b.dept_no
and a.salary between 10000 and 20000
and a.start_date between date1 and date2 
and a.end_date between date3 and date4

【讨论】:

    【解决方案2】:

    我相信您的过程比您的 SQL 查询运行得慢,因为您以错误的顺序使用了括号。在 SQL 中,salary、start-end_date 需要适合,在程序salary 中,start-ORend_date。更多行,更多时间。

    其次,min_x IS NULL or max_x IS NULL or x BETWEEN min_x AND min_y 语法可能会导致不同的执行计划。我用一个最小的例子对此进行了测试:

    CREATE TABLE t AS SELECT * FROM all_objects;
    CREATE INDEX i ON t(created);
    EXEC DBMS_STATS.GATHER_TABLE_STATS(null, 't');
    
    SELECT /* my_static */ count(*) 
      FROM t 
      WHERE created BETWEEN DATE '2020-06-06' AND DATE '2020-06-07';
    
    CREATE OR REPLACE PROCEDURE p(d1 DATE, d2 DATE, tag VARCHAR2) 
    AS
      stmt VARCHAR2(1000);
      n NUMBER;
    BEGIN
      stmt := 'SELECT /* '||tag||' */ count(*) FROM t 
                WHERE created BETWEEN :d1 AND :d2';
      EXECUTE IMMEDIATE stmt INTO n USING d1, d2;
    END p;
    /
    
    CREATE OR REPLACE PROCEDURE p2(d1 DATE, d2 DATE, tag VARCHAR2) 
    AS
      stmt VARCHAR2(1000);
      n NUMBER;
    BEGIN
      stmt := 'SELECT /* '||tag||' */ count(*) FROM t 
                WHERE :d1 IS NULL OR :d2 IS NULL OR created BETWEEN :d1 AND :d2';
      EXECUTE IMMEDIATE stmt INTO n USING d1, d2, d1, d2;
    END p2;
    /
    
    EXEC  p(DATE '2020-06-06', DATE '2020-06-07', 'my_dynamic1');
    EXEC p2(DATE '2020-06-06', DATE '2020-06-07', 'my_dynamic2');
    

    查找存储过程的执行计划有点复杂,您需要在V$SQLTEXT 中挖掘以找到您的sql_id,然后输入DBMS_XPLAN

    SELECT * FROM V$SQLTEXT 
     where sql_text LIKE ('%my_static%') 
        or sql_text LIKE ('%my_dynamic%');
    
    SELECT * FROM table(DBMS_XPLAN.DISPLAY_CURSOR('7agywn91wbmt3',0));
    

    SQL 和过程 p1 都有相同的执行计划

    ---------------------------------------------------------------------------
    | Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
    ---------------------------------------------------------------------------
    |   0 | SELECT STATEMENT   |      |       |       |     2 (100)|          |
    |   1 |  SORT AGGREGATE    |      |     1 |     8 |            |          |
    |*  2 |   FILTER           |      |       |       |            |          |
    |*  3 |    INDEX RANGE SCAN| I    | >>325<|  2600 |     2   (0)| 00:00:01 |
    ---------------------------------------------------------------------------
    

    但是,过程 p2 有一个不同的计划,它需要扫描 6840 个索引行而不是 325 个:

    ------------------------------------------------------------------------------
    | Id  | Operation             | Name | Rows  | Bytes | Cost (%CPU)| Time     |
    ------------------------------------------------------------------------------
    |   0 | SELECT STATEMENT      |      |       |       |    51 (100)|          |
    |   1 |  SORT AGGREGATE       |      |     1 |     8 |            |          |
    |*  2 |   INDEX FAST FULL SCAN| I    |>>6840<| 54720 |    51   (2)| 00:00:01 |
    ------------------------------------------------------------------------------
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-01-05
      • 2011-02-19
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多