Оптимизация “исторического” отчета в Oracle

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

CREATE TABLE MAIN_TABLE
(
  ID NUMBER NOT NULL,
  SUMMA NUMBER NOT NULL
,CONSTRAINT MAIN_TABLE_PK PRIMARY KEY (ID) ENABLE
);

У этой таблицы есть куча дочерних таблиц и функция расчета некоего остатка денег
CalcRest(id in number,ReportDate in date), собирающая информацию по всем дочерним
таблицам на определенную дату и довольно сложно ее обрабатывающая. Нам нужно получить
отчет содержащий ID, SUMMA и результат CalcRest.

Итерация 0

select id, summa, CalcRest(id,:ReportDate) rest
  from main_table;
Предположим, что в таком виде отчет считается 10 часов – все время уходит на запуск 
CalcRest для каждой из 10000 записей.

Итерация 1

Добавляем к таблице поле для сохранения последнего значения остатка и триггеры для всех 
дочерних таблиц для обновления этого поля:

ALTER TABLE MAIN_TABLE
  ADD ("REST" NUMBER DEFAULT 0 NOT NULL);

create or replace trigger detail_table1_rest
after insert or delete or update on detail_table1
for each row
begin
  —MAIN_ID — ID из MAIN_TABLE в дочерних таблицах
  update main_table
    set rest=CalcRest(:new.main_id, trunc(sysdate))
    where id=:new.main_id;
end;

Теперь запрос для отчета выглядит так:
select id, summa, 
(case when :ReportDate=trunc(sysdate) then rest
else CalcRest(id, :ReportDate) end) rest
from main_table;

Что существенно ускоряет работу отчета, если мы его формируем на текущую дату, для всех остальных дат – 10 часов.

Итерация 2

Добавляем поле с датой изменения остатка и модифицируем триггера:

ALTER TABLE MAIN_TABLE
  ADD ("REST_DATE" DATE DEFAULT sysdate NOT NULL);

create or replace trigger detail_table1_rest
after insert or delete or update on detail_table1
for each row
begin
  —MAIN_ID — ID из MAIN_TABLE в дочерних таблицах
  update main_table
    set rest=CalcRest(:new.main_id, trunc(sysdate)),
        rest_date=sysdate
    where id=:new.main_id;
end;

И запрос:

select id, summa,
(case when :ReportDate=trunc(sysdate) or :ReportDate>rest_date then rest
else CalcRest(id, :ReportDate) end) rest
from main_table;

В результате CalcRest вызывается только для записей у которых остаток менялся после той даты на которую мы формируем отчет.

PS: Получилось немного сумбурно, но данный алгоритм реально работает.

Реклама
Запись опубликована в рубрике Uncategorized. Добавьте в закладки постоянную ссылку.

Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход /  Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход /  Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход /  Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход /  Изменить )

Connecting to %s