2011-04-06 2 views
3

я быть_наст в процедуре, которая заполняет таблицу следующего SQLNVL или исключение NO_DATA_FOUND

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 для обоих столбцов.

ли лучше использовать исключение handeling как:

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; 

Спасибо.

+0

Теоретически COALESCE должен быть быстрее, чем NVL, потому что первый выполняется SQL-машиной, а второй - pl/sql. Но я сомневаюсь, что вы можете измерить разницу, тем более, что она вызывается только в конце запроса, а не раз для каждой строки. Также см. Ответ Джастина Кейва, потому что ваш код никогда не будет поднимать «Нет данных». – redcayuga

ответ

7

Эти два блока делают совершенно разные вещи. Ваш оператор 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

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

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

YMMV.

+0

Согласитесь с вашей точкой, но в этом случае использование обработчика исключений даже не дает того же результата. –

2

NO_DATA_FOUND будет выброшен, если строки не были возвращены, НЕ, если в фактических строках, возвращаемых из запроса, возвращены значения NULL. Это бросить 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; 

Нет циклов, в, сделать одним махом. FYI, вы могли бы установить 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 присоединиться к результату (из-за дополнительные не нулевые положения в запросе).

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

2

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

Сначала нужно создать две таблицы с 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> 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 должен присоединиться к 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. 

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

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

С уважением,
Rob.

+0

Кроме того, индекс на col1/col2 может ускорить работу даже больше. –

+0

Нет, это не так: http://download.oracle.com/docs/cd/E11882_01/server.112/e16638/optimops.htm#PFGRF94619 –

+0

Только если вы знаете, что значения всегда будут неотрицательными, тогда вы можно переписать предикаты на col1> 0 и col2> 0, и в этом случае вы можете использовать индекс. –

Смежные вопросы