Sql NVL或异常未找到数据

Sql NVL或异常未找到数据,sql,performance,oracle,exception-handling,plsql,Sql,Performance,Oracle,Exception Handling,Plsql,我在一个过程中填充了一个表,其中包含以下sql SELECT NVL(SUM(COL1), 0), NVL(SUM(COL2), 0) INTO v_mytable.COLUMN1, v_mytable.COLUMN2 FROM t1, t2 WHERE t1.id = t2.id AND t1.date = t2.date 此外,对于99%的表行,这些列=0,并且在大多数情况下,当两列都返回0时,执行此查询需要很长时间 使用异常处理是否更好,如下所

我在一个过程中填充了一个表,其中包含以下sql

SELECT NVL(SUM(COL1), 0),
       NVL(SUM(COL2), 0)
  INTO v_mytable.COLUMN1,
       v_mytable.COLUMN2
  FROM t1, t2
 WHERE t1.id = t2.id
   AND t1.date = t2.date
此外,对于99%的表行,这些列=0,并且在大多数情况下,当两列都返回0时,执行此查询需要很长时间

使用异常处理是否更好,如下所示:

BEGIN
  SELECT SUM(COL1),
         SUM(COL2)
    INTO v_mytable.COLUMN1,
         v_mytable.COLUMN2
    FROM t1, t2
   WHERE t1.id = t2.id
     AND t1.date = t2.date
EXCEPTION WHEN NO_DATA_FOUND THEN
  v_mytable.COLUMN1 := 0 ;
  v_mytable.COLUMN2 := 0 ;
END;

谢谢。

考虑到这两种选择,我会选择第一种

我更喜欢对真正的异常/错误使用异常处理程序,而不是控制流


YMMV.

这两个区块的功能完全不同。如果COL1和/或COL2始终为空,则SELECT语句不会抛出“未找到数据”错误。它只需在v_mytable.COLUMN1和v_mytable.COLUMN2中输入NULL

你可以

SELECT SUM(COL1),
       SUM(COL2)
  INTO v_mytable.COLUMN1,
       v_mytable.COLUMN2
  FROM t1, t2
 WHERE t1.id = t2.id
   AND t1.date = t2.date

v_mytable.COLUMN1 := NVL( v_mytable.COLUMN1, 0 );
v_mytable.COLUMN2 := NVL( v_mytable.COLUMN2, 0 );

但是,我不认为这会更快。

如果没有返回行,则不会抛出找到的任何数据,如果查询返回的实际行中返回了null值,则不会抛出。这将抛出找不到的数据:

select sysdate
into myVariable
from dual
where 1=0;
select null
into myVariable
from dual;
这不会抛出未找到的数据:

select sysdate
into myVariable
from dual
where 1=0;
select null
into myVariable
from dual;

这样说,如果您只想忽略COL1和COL2为空的行,那么可以考虑在PL/SQL中使用集合,并使用批量收集,例如:

select sum(col1) as sum_col1, sum(col2) as sum_col2, col3
bulk collect into v_mytable
FROM t1, t2
 WHERE t1.id = t2.id
   AND t1.date = t2.date
   AND col1 is not null
   AND col2 is not null
GROUP by col3;
declare
  type t_rec is record
  (col1_sum number,
   col2_sum number,
   col3 number);
  v_rec t_rec;

  type t_tab is table of v_rec%type;
  v_mytable t_tab;

begin
...
没有循环,一下子就做到了。仅供参考,您可以将v_mytable设置为:

select sum(col1) as sum_col1, sum(col2) as sum_col2, col3
bulk collect into v_mytable
FROM t1, t2
 WHERE t1.id = t2.id
   AND t1.date = t2.date
   AND col1 is not null
   AND col2 is not null
GROUP by col3;
declare
  type t_rec is record
  (col1_sum number,
   col2_sum number,
   col3 number);
  v_rec t_rec;

  type t_tab is table of v_rec%type;
  v_mytable t_tab;

begin
...
稍后,您可以循环遍历v_mytable,由于查询中增加了NOTNULL子句,该值仅为原始t1、t2连接结果的1%


希望这会有所帮助。

如果停止连接col值为0的行,SQL将运行得更快。下面是一个小测试来证明我的观点

首先创建两个包含100000行的表,其中99%的行的列值设置为0:

SQL> create table t1 (id,date1,col1)
  2  as
  3   select level
  4        , trunc(sysdate)
  5        , case mod(level,100) when 42 then 42 else 0 end
  6     from dual
  7  connect by level <= 100000
  8  /

Table created.

SQL> create table t2 (id,date2,col2)
  2  as
  3   select level
  4        , trunc(sysdate)
  5        , case mod(level,100) when 42 then 84 else 0 end
  6     from dual
  7  connect by level <= 100000
  8  /

Table created.
并在运行查询时收集统计信息:

SQL> set serveroutput off
SQL> alter session set statistics_level = all
  2  /

Session altered.
现在,您的查询运行方式如下:

SQL> SELECT NVL(SUM(t1.COL1), 0)
  2       , NVL(SUM(t2.COL2), 0)
  3    FROM t1
  4       , t2
  5   WHERE t1.id = t2.id
  6     AND t1.date1 = t2.date2
  7  /

NVL(SUM(T1.COL1),0) NVL(SUM(T2.COL2),0)
------------------- -------------------
              42000               84000

1 row selected.

SQL> select * from table(dbms_xplan.display_cursor(null,null,'allstats last'))
  2  /

PLAN_TABLE_OUTPUT
-----------------------------------------------------------------------------------------------------------------
SQL_ID  6q5h7h8ht5232, child number 0
-------------------------------------
SELECT NVL(SUM(t1.COL1), 0)      , NVL(SUM(t2.COL2), 0)   FROM t1      , t2  WHERE t1.id = t2.id    AND
t1.date1 = t2.date2

Plan hash value: 446739472

-----------------------------------------------------------------------------------------------------------------
| Id  | Operation           | Name | Starts | E-Rows | A-Rows |   A-Time   | Buffers |  OMem |  1Mem | Used-Mem |
-----------------------------------------------------------------------------------------------------------------
|   1 |  SORT AGGREGATE     |      |      1 |      1 |      1 |00:00:00.37 |     560 |       |       |          |
|*  2 |   HASH JOIN         |      |      1 |    100K|    100K|00:00:00.24 |     560 |  4669K|  1437K| 7612K (0)|
|   3 |    TABLE ACCESS FULL| T1   |      1 |    100K|    100K|00:00:00.01 |     280 |       |       |          |
|   4 |    TABLE ACCESS FULL| T2   |      1 |    100K|    100K|00:00:00.01 |     280 |       |       |          |
-----------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - access("T1"."ID"="T2"."ID" AND "T1"."DATE1"="T2"."DATE2")


21 rows selected.
您可以看到,散列连接需要连接100K行,这是花费大部分时间的地方。现在排除0值:

SQL> SELECT NVL(SUM(t1.COL1), 0)
  2       , NVL(SUM(t2.COL2), 0)
  3    FROM t1
  4       , t2
  5   WHERE t1.id = t2.id
  6     AND t1.date1 = t2.date2
  7     and t1.col1 != 0
  8     and t2.col2 != 0
  9  /

NVL(SUM(T1.COL1),0) NVL(SUM(T2.COL2),0)
------------------- -------------------
              42000               84000

1 row selected.

SQL> select * from table(dbms_xplan.display_cursor(null,null,'allstats last'))
  2  /

PLAN_TABLE_OUTPUT
-----------------------------------------------------------------------------------------------------------------
SQL_ID  bjr7wrjx5tjvr, child number 0
-------------------------------------
SELECT NVL(SUM(t1.COL1), 0)      , NVL(SUM(t2.COL2), 0)   FROM t1      , t2  WHERE t1.id = t2.id    AND
t1.date1 = t2.date2    and t1.col1 != 0    and t2.col2 != 0

Plan hash value: 446739472

-----------------------------------------------------------------------------------------------------------------
| Id  | Operation           | Name | Starts | E-Rows | A-Rows |   A-Time   | Buffers |  OMem |  1Mem | Used-Mem |
-----------------------------------------------------------------------------------------------------------------
|   1 |  SORT AGGREGATE     |      |      1 |      1 |      1 |00:00:00.02 |     560 |       |       |          |
|*  2 |   HASH JOIN         |      |      1 |  25000 |   1000 |00:00:00.02 |     560 |  1063K|  1063K| 1466K (0)|
|*  3 |    TABLE ACCESS FULL| T1   |      1 |  50000 |   1000 |00:00:00.01 |     280 |       |       |          |
|*  4 |    TABLE ACCESS FULL| T2   |      1 |  50000 |   1000 |00:00:00.01 |     280 |       |       |          |
-----------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - access("T1"."ID"="T2"."ID" AND "T1"."DATE1"="T2"."DATE2")
   3 - filter("T1"."COL1"<>0)
   4 - filter("T2"."COL2"<>0)


23 rows selected.
您可以看到散列连接现在只需要连接1000行,从而获得更快的输出

希望这有帮助

问候,,
Rob.

理论上,COALESCE应该比NVL更快,因为前者由SQL引擎执行,后者由pl/SQL执行。但我怀疑您是否能够度量差异,特别是因为它只在查询结束时被调用,而不是每行调用一次。另外,请参见Justin Cave的答案,因为您的代码永远不会引发未找到的数据。同意您的观点,但在这种情况下,使用异常处理程序甚至不会产生相同的结果。此外,col1/col2上的索引可以加快速度。不,它不会:只有当您知道值始终为非负值时,然后你可以将谓词重写为COL1> 0和COL2> 0,在这种情况下,你可以使用一个索引。