Настройка запросов в хранимой процедуре - PullRequest
0 голосов
/ 16 апреля 2019

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

Procedure status(p_order_id in varchar2(25), p_stat out sys_refcursor) IS

Begin
open p_stat

select o.status_code,o.order_id FROM order o
where o.order_id=p_order_id 
union
select a.status_code,a.order_id FROM order_archive a
where a.order_id=p_order_id ;

end status;

Ответы [ 2 ]

1 голос
/ 16 апреля 2019

Вы можете добавить проверку not exists к основной таблице как часть ветви архива:

select o.status_code,o.order_id
from order o
where o.order_id=p_order_id 
union all
select a.status_code,a.order_id
from order_archive a
where a.order_id=p_order_id
and not exists (
  select *
  from order o
  where o.order_id=p_order_id 
);

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


Другой вариант, который предотвращает просмотр архивной таблицы при наличии идентификатора в основной таблице, - это сначала сделать простой подсчет по основной таблице, а затем условно открыть курсор ref для одного из двух возможных запросов к один стол каждый; то есть ветвление в PL / SQL, а не в SQL, полностью устраняя объединение. Что-то вроде:

procedure status(p_order_id in varchar2, p_stat out sys_refcursor) is
  l_count pls_integer;
begin
  select count(*) into l_count
  from order o
  where o.order_id=p_order_id;

  if l_count > 0 then
    -- exists in main table so only query that
    open p_stat for
      select o.status_code,o.order_id
      from order o
      where o.order_id=p_order_id;
  else
    -- does not exist in main table so only query archive
    open p_stat for
      select a.status_code,a.order_id
      from order_archive a
      where a.order_id=p_order_id;
  end if;
end status;
/

Если идентификатор не является уникальным в основной таблице, вы можете добавить and rownum = 1 к исходному запросу, чтобы он останавливался, как только обнаруживал какие-либо подходящие строки (и, следовательно, l_count может быть не более 1) вместо получить точный подсчет, так как вы на самом деле не заботитесь о фактическом найденном числе.

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

Не совсем, потому что выборка будет занимать первый ряд результатов. Вот очень надуманный пример:

var rc refcursor;

declare
  x varchar2(1);
begin
  open :rc for
    select 'A' from dual where 1 = 0
    union all
    select 'B' from dual where 1 = 0;
  fetch :rc into x;
  if :rc%notfound then
    open :rc for select 'C' from dual;
  end if;
end;
/

PL/SQL procedure successfully completed.

print rc

'
-
C

При такой настройке основной запрос не находит строк (из-за проверки 1 = 0); после выборки курсор равен notfound, поэтому он снова открывает курсор для запроса «архив», и вызывающая сторона видит это, как и ожидалось.

Но если первый запрос возвращает строки:

declare
  x varchar2(1);
begin
  open :rc for
    select 'A' from dual where 1=1
    union all
    select 'B' from dual where 1=1;
  fetch :rc into x;
  if :rc%notfound then
    open :rc for select 'C' from dual;
  end if;
end;
/

PL/SQL procedure successfully completed.

print rc

'
-
B

затем после выборки курсор равен found, поэтому он не открывает курсор для запроса архива, и существующий курсор ref возвращается обратно вызывающей стороне. Но первая строка из курсора уже была извлечена в переменную x и потеряна, поэтому вызывающая сторона больше не видит этого. print показывает только одну строку с 'B', а другая строка, которая должна быть там с 'A', отсутствует.

Чтобы вызывающая сторона все еще видела обе строки, вам придется заново открыть курсор с помощью основного запроса:

declare
  x varchar2(1);
begin
  open :rc for
    select 'A' from dual where 1=1
    union all
    select 'B' from dual where 1=1;
  fetch :rc into x;
  if :rc%found then
    open :rc for
      select 'A' from dual where 1=1
      union all
      select 'B' from dual where 1=1;
  else
    open :rc for select 'C' from dual;
  end if;
end;
/

PL/SQL procedure successfully completed.

print rc

'
-
A
B

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

0 голосов
/ 16 апреля 2019

Первый шаг прост в использовании UNION ALL, потому что UNION гарантирует distinct результат обеих таблиц.

Использование UNION ALL будет извлекать данные из первой таблицы и только послепри извлечении всех строк из него он получит доступ ко второй таблице.

UNION потребуется только в том случае, если строки идентичны с order_id и status_code в обеих таблицах, и вам необходимо их дедуплицировать;что не является типичным сценарием.

...