NVL или ИСКЛЮЧЕНИЕ NO_DATA_FOUND - PullRequest
       14

NVL или ИСКЛЮЧЕНИЕ NO_DATA_FOUND

3 голосов
/ 06 апреля 2011

У меня есть процедура, которая заполняет таблицу следующим 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;

Спасибо.

Ответы [ 4 ]

7 голосов
/ 06 апреля 2011

Эти два блока делают совершенно разные вещи.Ваш оператор SELECT не будет выдавать ошибку NO_DATA_FOUND, если COL1 и / или COL2 всегда равны NULL.Было бы просто положить NULL в v_mytable.COLUMN1 и v_mytable.COLUMN2.

Вы можете сделать

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 );

Однако я не ожидаю, что это будет быстрее.

3 голосов
/ 06 апреля 2011

Учитывая выбор между этими двумя, я бы выбрал первый.

Я предпочитаю использовать обработчики исключений для подлинных исключений / ошибок, а не для потока управления.

YMMV.

2 голосов
/ 07 апреля 2011

Ваш SQL будет работать намного быстрее, если вы перестанете объединять строки, для которых значения col равны 0. Ниже приведен небольшой тест, чтобы доказать мою точку зрения.

Сначала создайте две таблицы с 100 000 строк, где 99% столбцов имеют значение col, установленное в 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> exec dbms_stats.gather_table_stats(user,'t1')

PL/SQL procedure successfully completed.

SQL> exec dbms_stats.gather_table_stats(user,'t2')

PL/SQL procedure successfully completed.

И собирает статистику при выполнении запросов:

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.

Вы можете видеть, что HASH JOIN должен объединять 100К строк, и именно здесь тратится большая часть времени.Теперь исключите 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.

И вы можете видеть, что HASH JOIN теперь нужно только объединить 1000 строк, что приводит к гораздо более быстрому выводу.

Надеюсь, это поможет.

С уважением,
Роб.

2 голосов
/ 06 апреля 2011

NO_DATA_FOUND будет выброшено, если не было возвращено ни одной строки, НЕ, если нулевые значения были возвращены в фактических строках, которые ARE вернул из запроса.Это выкинет NO_DATA_FOUND:

select sysdate
into myVariable
from dual
where 1=0;

Это НЕ выкинет NO_DATA_FOUND:

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;

Без циклов, сделайте одним махом.К вашему сведению, вы бы настроили v_mytable что-то вроде:

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, который будет составлять только 1% от исходного результата соединения t1, t2 (из-за дополнительных ненулевых предложений в запросе),

Надеюсь, это поможет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...