Уважаемые подписчики! Этот выпуск посвящен одному из вопросов,
на которые ответилв начале месяца Том Кайт на сайте
asktom.oracle.com.
Обещанные комментарии к сценарию, с помощью которого Том получает такие
красивые приглашения в SQL*Plus, как и о самом SQL*Plus - в следующем выпуске.
Дядю Тома спросили об оптимизации запроса:
Как оптимизировать запрос с конструкцией where следующего вида
select * from document where (documentid=1234 and version number =1.1)
or (documnetid=1233 and versionnumber=1.2) or...
и так далее для примерно 1500 документов, 1500 условий or.
Этот запрос выполняется около 3,5 минут.
и он ответил
Надеюсь, на самом деле вы не указывали литералы в запросе -- одно это
будет жестоким ударом по производительности. Во всех подобных запросах надо
использовать связываемые переменные.
Я рекомендую не использовать кучу операторов OR или AND, а вместо
этого сделать примерно так:
create global temporary table t ( docid number, version number )
on commit delete rows;
а затем, перед выполнением запроса, наполнять временную таблицу данными
(используя, по возможности, обработку массивов и обязательно, - ОБЯЗАТЕЛЬНО -
связываемые переменные). Затем выполнить следующий простой запрос:
select *
from document
where (documentid,versionnumber) in (select docid,version from t );
Если не хотите использовать временную таблицу, я бы все равно
использовал конструкцию IN, но делал бы это примерно так:
ops$tkyte@ORA8I.WORLD> create or replace type myScalarType as object
2 ( docid number, version number )
3 /
Type created.
ops$tkyte@ORA8I.WORLD>
ops$tkyte@ORA8I.WORLD> create or replace type myTableType
2 as table of myScalarType
3 /
Type created.
ops$tkyte@ORA8I.WORLD>
ops$tkyte@ORA8I.WORLD>
ops$tkyte@ORA8I.WORLD> create or replace
2 function in_list( p_string in varchar2 )
3 return myTableType
4 as
5 l_string long default p_string || '|';
6 l_data myTableType := myTableType();
7 n number;
8 l_piece varchar2(255);
9 begin
10 loop
11 exit when l_string is null;
12 n := instr( l_string, '|' );
13 l_data.extend;
14 l_piece := substr( l_string, 1, n-1 );
15 l_string := substr( l_string, n+1 );
16
17 n := instr( l_piece,',');
18 l_data(l_data.count) :=
19 myScalarType( substr(l_piece,1,n-1),
20 substr(l_piece,n+1) );
21 end loop;
22
23 return l_data;
24 end;
25 /
Function created.
ops$tkyte@ORA8I.WORLD>
ops$tkyte@ORA8I.WORLD>
ops$tkyte@ORA8I.WORLD> drop table document;
Table dropped.
ops$tkyte@ORA8I.WORLD> create table document ( docid number, version number );
Table created.
ops$tkyte@ORA8I.WORLD>
ops$tkyte@ORA8I.WORLD> insert into document values ( 1, 1 );
1 row created.
ops$tkyte@ORA8I.WORLD> insert into document values ( 2, 2 );
1 row created.
ops$tkyte@ORA8I.WORLD> insert into document values ( 3, 3 );
1 row created.
ops$tkyte@ORA8I.WORLD>
ops$tkyte@ORA8I.WORLD> variable str varchar2(4000);
ops$tkyte@ORA8I.WORLD> exec :str := '1,1|3,3'
PL/SQL procedure successfully completed.
ops$tkyte@ORA8I.WORLD>
ops$tkyte@ORA8I.WORLD> select *
2 from document
3 where (docid,version) in
4 ( select *
5 from TABLE(cast (in_list(:str) as myTableType)
6 )
7 )
8 /
DOCID VERSION
---------- ----------
1 1
3 3
ops$tkyte@ORA8I.WORLD>
Обсуждение
Сравните с точки зрения накладных расходов. Комментарий SK 2 июля 2002 года
Привет, Том!
Как эти два подхода (использование временной таблицы и
использование табличного типа) соотносятся с точки зрения накладных
расходов? В своей функции я использую временную таблицу.
Ответ дяди Тома
Измерьте производительность! Я бы сделал именно так
(я бы использовал массив для небольших объемов данных и
временную таблицу, если данных побольше).
Оригинал обсуждения этого вопроса можно найти
здесь.
Некоторые несущественные детали и комментарии читателей не переведены.
В следующем выпуске
Как получить информативное приглашение в командной строке SQL*Plus. Том Кайт о мутирующих таблицах.
Следите за новостями на сайте проекта Open
Oracle.