ORACLE, PLSQL, выбор из предварительно заполненного набора значений в предложении WHERE IN

Краткий пример и объяснение

Я хочу написать предложение WHERE IN, которое выбирает из предварительно заполненного набора чисел

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

ARRAY_OF_NUMBERS = Values from some select statement


  -- SHIPMENTS CURSOR
  OPEN O_SHIPMENTS_CURSOR FOR
       SELECT *
         FROM Q194977.AN_SHIPMENT_INFO SI
        WHERE INTERNAL_ASN IN (ARRAY_OF_NUMBERS) -- need to populate something
     ORDER BY INTERNAL_ASN;

  -- ORDER CURSOR
  OPEN O_ORDERS_CURSOR FOR
       SELECT *
         FROM Q194977.AN_ORDER_INFO OI
        WHERE INTERNAL_ASN IN (ARRAY_OF_NUMBERS) -- need to populate something
     ORDER BY INTERNAL_ASN;

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

Любые идеи о том, как лучше всего это сделать?

------------- РЕДАКТИРОВАТЬ ------------ (Добавление подробного примера)

Подробный пример и объяснение

У меня есть 4 таблицы на 4 разных иерархических уровнях и 4 хранимые процедуры. Каждая процедура содержит входные критерии для построения выборки данных на всех 4-х уровнях по критериям определенного уровня.

В этом примере мой вызывающий абонент введет критерии выбора, которые существуют на уровне коробки. Затем я буду использовать номера INTERNAL_ASN, суженные из этого выбора, для перемещения вверх по иерархическим уровням и извлечения: ЗАКАЗЫ, на которых находится эта коробка, ПОСТАВКИ, на которых находится ЗАКАЗ, а затем вниз, чтобы получить: ПРЕДМЕТЫ в этой КОРОБКЕ.

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

      -- SHIPMENTS CURSOR
  OPEN O_SHIPMENTS_CURSOR FOR
       SELECT *
         FROM Q194977.AN_SHIPMENT_INFO SI
        WHERE     INTERNAL_ASN IN
                     (SELECT INTERNAL_ASN
                        FROM Q194977.AN_CARTON_INFO CI
                       WHERE     (I_BOL IS NULL OR BILL_OF_LADING = I_BOL)
                             AND (   I_CARTON_NO IS NULL
                                  OR CARTON_NO = I_CARTON_NO)
                             AND (I_PO_NO = 0 OR PO_NO = I_PO_NO)
                             AND (I_STORE_NO = 0 OR STORE_NO = I_STORE_NO))
     ORDER BY INTERNAL_ASN;

  -- ORDER CURSOR
  OPEN O_ORDERS_CURSOR FOR
       SELECT *
         FROM Q194977.AN_ORDER_INFO OI
        WHERE     INTERNAL_ASN IN
                     (SELECT INTERNAL_ASN
                        FROM Q194977.AN_CARTON_INFO CI
                       WHERE     (I_BOL IS NULL OR BILL_OF_LADING = I_BOL)
                             AND (   I_CARTON_NO IS NULL
                                  OR CARTON_NO = I_CARTON_NO)
                             AND (I_PO_NO = 0 OR PO_NO = I_PO_NO)
                             AND (I_STORE_NO = 0 OR STORE_NO = I_STORE_NO))
              AND (I_PO_NO = 0 OR PO_NO = I_PO_NO)
     ORDER BY INTERNAL_ASN;

  -- CARTONS CURSOR
  OPEN O_CARTONS_CURSOR FOR
       SELECT *
         FROM Q194977.AN_CARTON_INFO CI
        WHERE     (I_BOL IS NULL OR BILL_OF_LADING = I_BOL)
              AND (I_CARTON_NO IS NULL OR CARTON_NO = I_CARTON_NO)
              AND (I_PO_NO = 0 OR PO_NO = I_PO_NO)
              AND (I_STORE_NO = 0 OR STORE_NO = I_STORE_NO)
     ORDER BY INTERNAL_ASN;

  -- ITEMS CURSOR
  OPEN O_ITEMS_CURSOR FOR
       SELECT *
         FROM Q194977.AN_ITEM_INFO II
        WHERE     CARTON_NO IN
                     (SELECT CARTON_NO
                        FROM Q194977.AN_CARTON_INFO CI
                       WHERE     (I_BOL IS NULL OR BILL_OF_LADING = I_BOL)
                             AND (   I_CARTON_NO IS NULL
                                  OR CARTON_NO = I_CARTON_NO)
                             AND (I_PO_NO = 0 OR PO_NO = I_PO_NO)
                             AND (I_STORE_NO = 0 OR STORE_NO = I_STORE_NO))
     ORDER BY INTERNAL_ASN;

person SomeRandomDeveloper    schedule 14.04.2014    source источник
comment
Не уверен, что это в основном проблема терминологии. Похоже, вам нужен тип массива уровня схемы или базы данных, что означает что-то, созданное в SQL с помощью create type, а не объявленное в PL/SQL. Затем вы можете использовать вызов table() вместо in. Это то, что вы имели ввиду? Может быть, что-то вроде этого?   -  person Alex Poole    schedule 15.04.2014


Ответы (1)


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

CREATE OR REPLACE TYPE num_tbl
    AS TABLE OF NUMBER;

Затем в вашей процедуре

  l_nums num_tbl;
BEGIN
  SELECT some_number
    BULK COLLECT INTO l_nums
    FROM <<your query to get the numbers>>;

  <<more code>>

  OPEN O_SHIPMENTS_CURSOR FOR
       SELECT *
         FROM Q194977.AN_SHIPMENT_INFO SI
        WHERE INTERNAL_ASN IN (SELECT column_value
                                 FROM TABLE( l_nums )) 
     ORDER BY INTERNAL_ASN;

Это синтаксически допустимо. А вот будет ли он вам полезен на самом деле — это отдельный вопрос.

  • Коллекции хранятся в относительно дорогой памяти PGA на сервере базы данных. Если вы храните пару сотен чисел в коллекции, это, вероятно, не является большой проблемой. Если, с другой стороны, вы храните 10 или 100 МБ данных и запускаете их в нескольких сеансах, этот один бит кода может легко занять много ГБ ОЗУ на сервере базы данных, что приведет к множеству проблем с производительностью.
  • Перемещение больших объемов данных из SQL в PL/SQL, а затем обратно в SQL также может быть несколько проблематичным с точки зрения производительности — обычно более эффективно оставить все в SQL и позволить механизму SQL справиться с этим.
  • Если вы используете коллекцию таким образом, вы не позволяете оптимизатору учитывать порядки соединения и планы запросов, которые объединяют два запроса более эффективным образом. Если вы уверены, что наиболее эффективным является план, в котором небольшое количество значений internal_asn используется для проверки таблицы an_shipment_info с помощью индекса, это может не вызывать серьезных опасений. Однако, если вы не уверены в том, какой план запроса лучше всего, и особенно если ваши фактические запросы более сложны, чем опубликованные вами, вы можете помешать оптимизатору использовать наиболее эффективный план для каждого запроса.

Какую проблему вы пытаетесь решить? Вы говорите о том, что не хотите дублировать код. Это заставило бы меня заподозрить, что вам просто нужно представление, на которое вы можете ссылаться в своих запросах, а не повторять код для сложного оператора SQL. Но это предполагает, что проблема, которую вы пытаетесь решить, связана с элегантностью кода, которая может быть точной, а может и нет.

person Justin Cave    schedule 14.04.2014
comment
Спасибо. Сначала у меня было больше подробностей, и я удалил их, думая, что слишком усложняю свой вопрос. Я отредактировал и добавил его обратно внизу. В этом примере кода я вхожу с критериями для сужения результатов через уровень CARTON, а затем повторяю запрос, чтобы получить числа INTERNAL_ASN для перехода вверх и получения других уровней. Исходя из опыта разработки программного обеспечения, я всегда считаю, что дублировать код плохо, но, возможно, лучше хранить это в SQL, как вы говорили. Я не уверен. Этот выбор меняется каждый раз в зависимости от того, что вводится и какой уровень, поэтому я не вижу, чтобы представление работало. - person SomeRandomDeveloper; 15.04.2014
comment
О, и это, вероятно, набор чисел от 1 до 100, так что я не думаю, что проблема с памятью. - person SomeRandomDeveloper; 15.04.2014