oracle存储过程比普通查询花费更多的时间

ccrfmcuu  于 2021-08-09  发布在  Java
关注(0)|答案(2)|浏览(411)

我有一个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\u date,end\u date)中为所有列添加了索引
我觉得约会标准要花更多的时间。我在谷歌上搜索并修改了起始日期和结束日期的数据类型,使之成为varchar的日期,但仍然没有成功。为什么它在存储过程中花费了更多的时间,但作为独立运行只需几秒钟?

ruoxqz4g

ruoxqz4g1#

使用CTA测试您的查询。你们会发现,完成这项工作几乎需要同样的时间。当您只选择表时,几乎所有的代码编辑器都会首先带来第一个可用的结果。

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
cs7cruho

cs7cruho2#

我相信您的过程比sql查询运行得慢,因为您使用括号的顺序不对。在sql中,salary、start-and end \u date需要匹配,在过程salary中,start-or end \u 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 |
------------------------------------------------------------------------------

相关问题