В Oracle есть тип данных для того, что следует за оператором IN? - PullRequest
2 голосов
/ 08 апреля 2019

У меня есть вопрос об Oracle PL / SQL.

В процедуре один и тот же встроенный оператор SELECT неоднократно используется с предложениями WHERE в запросах:

...
where start_year in (
   SELECT MB_START FROM MEMBERS
)
...

Фактический встроенный оператор SELECTв предложении WHERE процедура более сложна и заменена на «SELECT MB_START FROM MEMBERS», чтобы облегчить понимание моего вопроса.И именно поэтому я задаю вопрос:

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

DECLARE
   start_years <type_for_IN_operator>;
BEGIN
   ...
   select mb_start into start_years from members;
   ...
   WHERE start_year in (start_years)
   ...
END;
/

Я много искал и не смог выяснить, поддерживает ли Oracle сохранение значений в переменной виспользоваться оператором IN.Если это поддерживается, каков тип данных для type_for_IN_operator?

Ответ на ответы и дополнительный вопрос:

Спасибо всем за ответы на вопрос.

Я спросилвопрос, потому что я заметил, что оператор IN принимает набор значений в различных формах, таких как

  • литералы: IN (2015, 2016, 2017)
  • столбец, выбранный изtable: IN (выберите mb_start из членов)
  • коллекция с помощью инструкции SELECT: in (выберите column_value из таблицы (collection))

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

Меня привлекает идея APC, особенно это чистый SQL и виртуальная таблица многоразового использования.Я попытался, но перешел к проблеме.

Первоначально скрипт был:

select t.col1, t.col2, ... from
(
  select ...
  from ...
  where start_year in (<that subquery>)
  union
  select ...
  from ...
  where start_year in (<that subquery>)
  union
  ...
) t
join ...
...

Теперь используем виртуальную таблицу и заменим первый оригинальный подзапрос:

with sqf as (
    SELECT MB_START FROM MEMBERS
)
select t.col1, t.col2, ... from
(
  select ...
  from ...
  where start_year in (select * from sqf)
  union
  select ...
  from ...
  where start_year in (<that subquery>)
  union
  ...
) t
join ...
...

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

Любой совет?

Ответы [ 4 ]

3 голосов
/ 08 апреля 2019

Любой запрос в oracle возвращает курсор на основе выбранных столбцов.Не имеет значения, вызываете ли вы его внутри условия IN.

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

CREATE TYPE MEMBER_TAB_TYPE AS TABLE OF DATE;

DECLARE 
  MB_START_TABLE MEMBER_TAB_TYPE;
BEGIN 
  SELECT MB_START BULK COLLECT INTO MB_START_TABLE FROM MEMBERS;
  ....
  WHERE START_YEAR IN (SELECT COLUMN_VALUE FROM TABLE(MB_START_TABLE));
  ....
END;

Вы можете использовать MB_START_TABLE столько раз, сколько хотите в своей программе, без фактического запроса в таблицу MEMBERS, например "SELECT COLUMN_VALUE FROM TABLE (MB_START_TABLE)" * всегда будет храниться локальнозначения.

2 голосов
/ 09 апреля 2019

Можно ли сохранить то, что возвращает вложенный подзапрос, в переменной

Вроде. Это предложение WITH, также известное как факторинг подзапроса. Это было частью возможности Oracle SQL с 9i.

Используя ваш опубликованный пример:

with sqf as (
    SELECT MB_START FROM MEMBERS
)
select * from your_table
where col1 in ( select * from sqf )
and col2 in  ( select * from sqf )
and col3 not in ( select * from sqf )
/

Предложение WITH выполняется один раз и повторно используется в последующих ссылках. Мы можем использовать его в предложении FROM, включая предложение FROM других факторов подзапроса.

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

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

Оба решения работают, но по стоимости.

Первоначальному коду для завершения запроса требуется 23 секунды. 40 секунд для факторизованного подзапроса. 52 секунды для использования коллекции (выберите значение столбца из таблицы (коллекции)). Кроме того, оригинальный код должен быть немного изменен, чтобы две альтернативы работали.

Исходный запрос работает нормально, и это его структура:

select col1, col2 from
(
   SELECT x.col_a, x.col_b, ... from ...
   where
   (
      ( conditions A AND condition B)
      OR
      ( condition C AND condition D AND condition E)
   )
   AND
   condition F
   UNION
   select B
   UNION
   select C
) x
JOIN ...
JOIN ...
...

Но как факторизованный подзапрос, так и альтернативы коллекции становятся засоренными, работают вечно, не приводя к результатам, если только SELECT до того, как первый оператор UNION не будет разделен на две части, есть еще один UNION:

select col1, col2 from
(
   SELECT x.col_a, x.col_b, ... from ...
   where
      conditions A AND condition B AND condition F
   UNION
   SELECT col_a, col_b, ... from ...
   where
      condition C AND condition D AND condition E AND condition F
   UNION
   select B
   UNION
   select C
) x
JOIN ...
JOIN ...
...

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

(Я пытался установить флажок, чтобы отметить обе альтернативы как решения, но stackoverflow не позволяет и переключается между ними. Извините за это, и спасибо всем за помощь.)

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

IN логически представляет собой серию = выражений с OR между ними, так что фактически вы запрашиваете тип данных для тестов на равенство.

В вашем примере это выглядит как mb_start - это date, поэтому вам нужно определить отдельный тип коллекции table of date, если у вас его еще нет, который затем можно использовать в выражении table() или member of.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...