русс | укр

Языки программирования

ПаскальСиАссемблерJavaMatlabPhpHtmlJavaScriptCSSC#DelphiТурбо Пролог

Компьютерные сетиСистемное программное обеспечениеИнформационные технологииПрограммирование

Все о программировании


Linux Unix Алгоритмические языки Аналоговые и гибридные вычислительные устройства Архитектура микроконтроллеров Введение в разработку распределенных информационных систем Введение в численные методы Дискретная математика Информационное обслуживание пользователей Информация и моделирование в управлении производством Компьютерная графика Математическое и компьютерное моделирование Моделирование Нейрокомпьютеры Проектирование программ диагностики компьютерных систем и сетей Проектирование системных программ Системы счисления Теория статистики Теория оптимизации Уроки AutoCAD 3D Уроки базы данных Access Уроки Orcad Цифровые автоматы Шпаргалки по компьютеру Шпаргалки по программированию Экспертные системы Элементы теории информации

Оптимизация запросов.


Дата добавления: 2015-08-31; просмотров: 1106; Нарушение авторских прав


Допустим, есть таблицы kk_op1 и kk_op2, которые надо соединить по ключам id.

 

Если мы хотим для каждой строки kk_op1 запрашивать значения из kk_op2, то можно использовать хинт use_nl (use nested loops). Неплохой результат можно получить, если есть индекс по kk_op2.id или в kk_op1 мало строк. Хинт ordered указывает порядок соединения таблиц, важно в секции from первой указать маленькую таблицу:

select /*+ ordered use_nl(kk_op1 kk_op2)*/

*

from kk_op1, kk_op2

where kk_op1.id = kk_op2.id;

 

Если мы хотим попробовать построить hash по таблицам, считая, что поиск по индексу будет неэффективным, то можно использовать хинт use_hash:

select /*+ use_hash(kk_op1 kk_op2)*/

*

from kk_op1, kk_op2

where kk_op1.id = kk_op2.id;

 

Если мы хотим явно указать, что таблицу kk_op1 нужно сканировать по индексу IX_KK_OP1, то можно использовать хинт index:

select /*+ index(kk_op1 ix_kk_op1)*/

*

from kk_op1;

 

Если в запросе таблицам даны алиасы, то именно их нужно использовать в хинте, иначе хинт не будет работать.

 

Оптимизация запросов с помощью хинтов и модификации метода соединения таблиц не так эффективна, как изменение логики самого запроса.

Имеет смысл соединять маленькие по объему данных подзапросы, а не большие таблицы. Например, вместо запроса:

 

select a.id, a.f1, b.id, b.id2, b.f2, c.id, c.dt

from a, b, c

where a.id = b.id

and b.id2 = func(c.id)

and a.f1 = 'ограничение выборки'

and nvl(b.f2, '*') <> 'ограничение выборки2'

and c.dt between to_date('начало ограничения') and

to_date('конец ограничения');

 

стоит пробовать использовать запрос:

 

with aa as

(select /*+ materialize*/

id, f1

from a

where f1 = 'ограничение выборки'),



bb as

(select /*+ materialize*/

id, id2, f2

from b

where f2 <> 'ограничение выборки2'

or f2 is null /*использование nvl меняем на использование is null*/),

cc as

(select /*+ materialize*/

id, func(c.id) func_id /*рассчитанное поле, по которому будем соединять, материализуем*/, dt

from c

where dt between to_date('начало ограничения') and

to_date('конец ограничения'))

select aa.id, aa.f1, bb.id, bb.id2, bb.f2, cc.id, cc.dt

from aa, bb, cc /*стараемся соединять маленькие таблицы, а не большие*/

where aa.id = bb.id

and bb.id2 = cc.func_id;

 

Автору нравится использование конструкции WITH и хинта /*+ materialize*/, которое позволяет по ходу выполнения SQL-запроса фактически заниматься ETL-шагами, не занимаясь написанием create table as select.

 

Если же производится не оптимизация SQL-запроса, а оптимизация PL/SQL процедуры загрузки, то имеет смысл под каждый WITH создать промежуточную таблицу.

Затем в процедуре загрузки следует очищать эту промежуточную таблицу и заново заливать ее данными.

При этом продолжительность заполнения промежуточной таблицы, а также количество вставленных в нее строк разумно логировать в таблицу с логами.

По логам можно выявлять узкие места в загрузке и продолжать оптимизировать именно те загрузки в промежуточные таблицы, которые выполняются медленно.

 

Приведем еще немного примеров того, как хинтовка запросов может менять их время выполнения.

Создадим таблицу из 1 000 000 клиентов:

create table kk_op_clients(clientid number, clientname varchar2(50), fld1 varchar2(50), fld2 varchar2(50), fld3 varchar2(50), fld4 varchar2(50), fld5 varchar2(50), fld6 varchar2(50), fld7 varchar2(50), fld8 varchar2(50), fld9 varchar2(50), fld10 varchar2(50));

create index ix_clients_clientid on kk_op_clients(clientid);

create sequence sq_kk_op_clients;

begin

for i in 1 .. 1000000 loop

insert into kk_op_clients

(clientid, clientname, fld1, fld2, fld3, fld4, fld5, fld6, fld7, fld8, fld9, fld10)

values

(sq_kk_op_clients.nextval, dbms_random.string('l', 15), dbms_random.string('l', 15), dbms_random.string('l', 15), dbms_random.string('l', 15), dbms_random.string('l', 15), dbms_random.string('l', 15), dbms_random.string('l', 15), dbms_random.string('l', 15), dbms_random.string('l', 15), dbms_random.string('l', 15), dbms_random.string('l', 15));

end loop;

commit;

end;

/

Создадим таблицу из 10 000 000 счетов (работает долго):

create table kk_op_accounts(accountid number, clientid number, account_comment varchar2(200), fld1 varchar2(50), fld2 varchar2(50), fld3 varchar2(50), fld4 varchar2(50), fld5 varchar2(50), fld6 varchar2(50), fld7 varchar2(50), fld8 varchar2(50), fld9 varchar2(50), fld10 varchar2(50));

create index ix_accounts_accountid on kk_op_accounts(accountid);

create index ix_accounts_clientid on kk_op_accounts(clientid);

create sequence sq_kk_op_accounts;

begin

for i in 1 .. 10000000 loop

insert into kk_op_accounts

(accountid, clientid, account_comment, fld1, fld2, fld3, fld4, fld5, fld6, fld7, fld8, fld9, fld10)

values

(sq_kk_op_accounts.nextval, trunc(100000 * dbms_random.value() + 1), dbms_random.string('l', 25), dbms_random.string('l', 25), dbms_random.string('l', 25), dbms_random.string('l', 25), dbms_random.string('l', 25), dbms_random.string('l', 25), dbms_random.string('l', 25), dbms_random.string('l', 25), dbms_random.string('l', 25), dbms_random.string('l', 25), dbms_random.string('l', 25));

end loop;

commit;

end;

/

Далее мы будем последовательно создавать таблицы с выборками из свежесозданных таблиц, используя различные хинтовки.

Время выполнения может быть разным в зависимости от производительности сервера. Предлагается самостоятельно записать его и сравнить результаты.

Предлагается также смотреть планы запросов в PL/SQL Developer с помощью File -> New -> Explain Plan Window.

 

Продемонстрируем, как можно сделать выборку по всем счетам 20% клиентов, используя разные хинтовки запроса:

create table kk_op11 as

select /*+ ordered use_nl(c a) index(c ix_clients_clientid) index(a ix_accounts_clientid)*/

a.accountid, a.clientid, a.account_comment

from kk_op_clients c, kk_op_accounts a

where mod(c.clientid, 5) = 0

and c.clientid = a.clientid;

 

create table kk_op12 as

select /*+ use_hash(c a) full(c) full(a)*/

a.accountid, a.clientid, a.account_comment

from kk_op_clients c, kk_op_accounts a

where mod(c.clientid, 5) = 0

and c.clientid = a.clientid;

 

create table kk_op13 as

select /*+ use_hash(c a) index(c ix_clients_clientid) full(a)*/

a.accountid, a.clientid, a.account_comment

from kk_op_clients c, kk_op_accounts a

where mod(c.clientid, 5) = 0

and c.clientid = a.clientid;

 

А теперь смотрим производительность таких запросов, когда нужно сделать выборку по всем счетам 1% клиентов:

create table kk_op21 as

select /*+ ordered use_nl(c a) index(c ix_clients_clientid) index(a ix_accounts_clientid)*/

a.accountid, a.clientid, a.account_comment

from kk_op_clients c, kk_op_accounts a

where mod(c.clientid, 100) = 0

and c.clientid = a.clientid;

 

create table kk_op22 as

select /*+ use_hash(c a) full(c) full(a)*/

a.accountid, a.clientid, a.account_comment

from kk_op_clients c, kk_op_accounts a

where mod(c.clientid, 100) = 0

and c.clientid = a.clientid;

 

create table kk_op23 as

select /*+ use_hash(c a) index(c ix_clients_clientid) full(a)*/

a.accountid, a.clientid, a.account_comment

from kk_op_clients c, kk_op_accounts a

where mod(c.clientid, 100) = 0

and c.clientid = a.clientid;

 

Скорее всего, результаты запроса покажут, что когда из большой таблицы kk_op_accounts нужно взять 1% строк, то эффективно обращение к индексу ix_accounts_clientid с помощью nested loops, а если нужно брать много строк, то индекс скорее помешает.

 



<== предыдущая лекция | следующая лекция ==>
Models. | Резюме для руководителей


Карта сайта Карта сайта укр


Уроки php mysql Программирование

Онлайн система счисления Калькулятор онлайн обычный Инженерный калькулятор онлайн Замена русских букв на английские для вебмастеров Замена русских букв на английские

Аппаратное и программное обеспечение Графика и компьютерная сфера Интегрированная геоинформационная система Интернет Компьютер Комплектующие компьютера Лекции Методы и средства измерений неэлектрических величин Обслуживание компьютерных и периферийных устройств Операционные системы Параллельное программирование Проектирование электронных средств Периферийные устройства Полезные ресурсы для программистов Программы для программистов Статьи для программистов Cтруктура и организация данных


 


Не нашли то, что искали? Google вам в помощь!

 
 

© life-prog.ru При использовании материалов прямая ссылка на сайт обязательна.

Генерация страницы за: 1.084 сек.