Ускорение Oracle запрос - PullRequest
1 голос
/ 20 марта 2020

Я пытаюсь найти решение для ускорения запроса, который у меня уже есть. Индексирование нескольких столбцов помогает, но не очень. В настоящее время для достижения всех результатов мне нужно выполнить запрос несколько раз с несколькими вариациями условия where, и это займет всего несколько часов, чтобы выполнить все.

Basi c пример того, чего я пытаюсь достичь:

select
    col1
   ,col2
   ,...
from table
where <few conditions>

Могу ли я получить результат в col1 от куда и для col2 от куда и некоторых других полей, но не добавив их в условие where? Я знаю, что есть возможность, но я не знаю, как искать пример.

Большое спасибо


Пример того, что я должен сделать сейчас

procedure trigger_reporting
is
    type id_tt is table of customers.id%type index by binary_integer;
    type org_tt is table of customers.org_nr%type index by binary_integer;

    lt_id   id_tt;
    lt_org  org_tt;

    l_total pls_integer;
    l_res1  pls_integer;
    l_res2  pls_integer;
    ...etc

    --Here I just give an example
    l_start_date    date := '01.02.2020';
    l_end_date  date := '29.02.2020';
begin
    select id, org_nr
    into lt_id, lt_org
    from customers
    where is_active = 1;

    if lt_id.count > 0 then
        for i in lt_id.first..lt_id.last loop
            select count(*)
            into l_total
            from invoices
            where customer_id = lt_id(i)
            and orgnr = lt_org(i)
            and some_date between l_start_date and l_end_date;

            select count(*)
            into l_res1
            from invoices
            where customer_id = lt_id(i)
            and orgnr = lt_org(i)
            and some_date between l_start_date and l_end_date
            and deleted = 0;

            select count(*)
            into l_res2
            from invoices
            where customer_id = lt_id(i)
            and orgnr = lt_org(i)
            and some_date between l_start_date and l_end_date
            and status = 'Something';

            ...etc
        end loop;
    end if;
end;

Ответы [ 2 ]

2 голосов
/ 20 марта 2020

Вы можете значительно упростить вашу процедуру, используя присущую SQL способность объединять таблицы, плюс условное агрегирование.

Это делает вашу процедуру примерно такой:

procedure trigger_reporting
is
    type id_tt is table of customers.id%type index by binary_integer;
    type org_tt is table of customers.org_nr%type index by binary_integer;

    lt_id   id_tt;
    lt_org  org_tt;

    l_total pls_integer;
    l_res1  pls_integer;
    l_res2  pls_integer;

    --here i just give an example
    l_start_date date := to_date('01.02.2020', 'dd.mm.yyyy'); -- always explicitly convert strings into dates.
    l_end_date   date := to_date('29.02.2020', 'dd.mm.yyyy');
begin

    select count(*) total,
           count(case when i.deleted = 0 then 1 end) res1,
           count(case when i.status = 'Something' then 1 end) res2
    into   l_total,
           l_res1,
           l_res2
    from   customer c
           inner join invoices i on c.id = i.customer_id
                                    and c.org_nr = i.orgnr
    where  c.is_active = 1
    and    i.some_date between l_start_date and l_end_date; -- did you really mean this? Does your some_date column contain dates set to midnight?
end;
/

NB a пара моментов:

  1. вы назначали строки датам в вашей процедуре - не делайте этого. Когда вы делаете это, вы заставляете код полагаться на параметр NLS_DATE_FORMAT, и он может не совпадать при выполнении кода. Вместо этого сообщите Oracle формат, в котором находится ваша дата-как-строка (либо по to_date(), либо по буквальному признаку DATE). Это делает ваш код гораздо более надежным.
  2. Когда вы проводите сравнение по датам, оно включает временную часть. Поэтому, если в ваших столбцах даты указано время, отличное от полуночи, выполнение <= 29/02/2020 не приведет к 10 часам 29 февраля 2020 года. Это может быть не проблемой для вас, в зависимости от ваших данных и требований, но это что-то подумайте, когда вы напишите SQL.
0 голосов
/ 20 марта 2020

Вы можете объединить запросы в один (по крайней мере, для примера, который вы показываете), например так (вы можете добавить условие для третьего запроса):

select count(*), sum(case when deleted=0 then 1 else 0 end) count_deleted
            into l_total, l_res1
            from invoices
            where customer_id = lt_id(i)
            and orgnr = lt_org(i)
            and some_date between l_start_date and l_end_date;

И это может быть еще быстрее , поскольку он удаляет внешнюю l oop, но вам нужно адаптировать свой код (не показан):

select c.id, c.org_nr,count(*) count_all, sum(case when i.deleted=0 then 1 else 0 end) cout_deleted
                from invoices i join customers c using (i.customer_id=c.id and i.orgnr=c.org_nr)
                where c.is_active=1
                and i.some_date between l_start_date and l_end_date
                group by c.id,c.org_nr;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...