Oracle FOR LOOP не повторяется в SYS_REFCURSOR

Вот процедура:

  1. Открытие курсора, а затем получение вывода запроса на выборку через массовый сбор.
  2. Проблема заключается в том, что все идентификаторы сохраняются при массовом сборе, но я не могу выполнить второй запрос выбора, используя переменную массового сбора в качестве входных данных. Он принимает во внимание только первый идентификатор вместо всех.
  3. OUTPUT должен быть SYS_REFCURSOR, пожалуйста, проясните, что мне здесь не хватает

тестовые данные для таблицы 1:

ID CURRENCY T_ID
10 GBP PB1
15 GBP RB
20 GBP CC
25 AUD DC

На основе t_id я извлекаю соответствующие идентификаторы, а затем использую эти идентификаторы в дальнейших операторах select for цикла.

ТЕКУЩИЙ ВЫВОД ПРОЦЕССА ЧЕРЕЗ SYS_REFCURSOR:

ID COUNTRY ACCOUNT
10 UK PB1

ОЖИДАЕМЫЙ РЕЗУЛЬТАТ:

ID COUNTRY ACCOUNT
10 UK PB1
15 Wales RB
20 SH CC
create or replace procedure myproc (i_id in varchar2, rc out sys_refcursor)
as
    cursor names_cur is
        select id from table1 where currency='GBP' and t_id=i_id;
    names_t names_cur%ROWTYPE;
    type names_ntt is table of names_t%TYPE;
    l_names names_ntt;
begin
    open names_cur;
    fetch names_cur bulk collect into l_names ; --Inside l_names (10,15 & 20) would be stored 
    close names_cur;
--iSSUE IS WITH BELOW FOR LOOP
    for cur in l_names.first..l_names.last loop
        open rc for --For the below select I want to iterate l_names so for the above scenario it should iterate thrice

        select s.id,s.country,s.account from table2 s where s.id=l_names(cur).id;
    end loop;

end myproc;

person Linnea    schedule 12.03.2021    source источник
comment
Некоторые тестовые данные были бы очень полезны. Я не понимаю, для чего вам нужна процедура. Например, для чего нужен l_names, если вы хотите вернуть курсор? Вы можете создать курсор только с запросом, и вы можете открыть его только один раз.   -  person William Robertson    schedule 12.03.2021
comment
обязательно добавлю данные сейчас   -  person Linnea    schedule 12.03.2021
comment
В вашей процедуре вы передаете идентификатор и выбираете только строки из таблицы 1 с этим идентификатором, но ожидаемый результат включает несколько идентификаторов, поэтому я до сих пор не понимаю, что вам нужно.   -  person William Robertson    schedule 13.03.2021
comment
@William Когда я передаю i_id (из Proc) в t_id (в качестве ввода) в таблице 1, на основе ввода будет получено несколько совпадающих идентификаторов на основе условий соединения, поэтому вы видите 3 идентификатора. 1. Таким образом, в основном извлекаются все совпадения идентификаторов на основе t_id, переданного процедуре. 2. Сохраните идентификаторы в некоторой переменной/коллекции. 3. Итерируйте идентификатор в следующих запросах выбора в качестве входных данных. 4. Сгенерируйте выходные данные в виде sys_refcursor или массивов.   -  person Linnea    schedule 13.03.2021


Ответы (2)


Обратите внимание на следующие расширенные комментарии:

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

open rc for select id from table1;

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


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

Если вы не можете создать тип, посмотрите, какие уже существующие типы можно использовать:

select owner, type_name
from   all_coll_types t
where  t.coll_type = 'TABLE'
and    t.elem_type_name = 'NUMBER';

Например:

create or replace type number_tt as table of number;

create table table1 (id primary key, currency, t_id) as
    select 10, 'GBP', 'PB1' from dual union all
    select 15, 'GBP', 'RB' from dual union all
    select 20, 'GBP', 'CC' from dual union all
    select 25, 'AUD', 'DC' from dual;

create table table2 (id,country,account) as
    select 10, 'UK', 'PB1' from dual union all
    select 15, 'Wales', 'RB' from dual union all
    select 20, 'SH', 'CC' from dual;

Теперь процедура может быть:

create or replace procedure myproc
    ( rc out sys_refcursor)
as
    l_names number_tt;
begin
    select id bulk collect into l_names
    from   table1
    where  currency = 'GBP';

    open rc for
        select t.id,t.country,t.account from table2 t
        where  t.id member of l_names;
end myproc;

Вывод курсора:

        ID COUNT ACC
---------- ----- ---
        10 UK    PB1
        15 Wales RB
        20 SH    CC

(Я удалил параметр i_id в вашей процедуре, так как не понял, как вы хотите его использовать.)

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

create or replace procedure myproc
    ( rc out sys_refcursor)
as
begin
    open rc for
        select t.id,t.country,t.account from table2 t
        where  t.id in
               ( select id 
                 from   table1
                 where  currency = 'GBP' );
end myproc;

или просто присоединяйтесь к нему, как предложил Литтлфут:

create or replace procedure myproc
    ( rc out sys_refcursor)
as
begin
    open rc for
        select t2.id, t2.country, t2.account
        from   table1 t1
               join table2 t2 on t2.id = t1.id
        where  t1.currency = 'GBP';
end myproc;

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

person William Robertson    schedule 13.03.2021
comment
У меня работает: dbfiddle.uk/ - person William Robertson; 13.03.2021
comment
Извинения, Уильям добавил лишний ; в моем коде. Однако у меня нет привилегий для запуска create or replace type number_tt as table of number; , похоже, администратор базы данных наложил некоторые ограничения. Не могли бы проверить, почему цикл for с SYS_REFCURSOR не повторяется? Если это будет решено, то мой вопрос будет дан ответ - person Linnea; 13.03.2021
comment
Я добавил запрос существующих типов коллекций, которые могут быть доступны, чтобы избавить вас от необходимости создавать свои собственные. - person William Robertson; 13.03.2021
comment
@Linnea - цикл повторяется. На каждой итерации вы открываете новый курсор, который неявно отбрасывает ранее открытый курсор. Таким образом, когда процедура возвращается, возвращается только последний открытый курсор. - person Justin Cave; 13.03.2021
comment
Правда, Джастин, только что понял, что пытался открыть SYS_REFCURSOR до цикла for, но это дает ошибку компиляции :( - person Linnea; 13.03.2021
comment
Вот собственно и весь смысл моего ответа. Не открывайте курсор для каждого значения в коллекции, по одному значению за раз. Откройте его только один раз, пройдя всю коллекцию. Я использовал member of, но другие варианты синтаксиса также будут работать (join, in (subquery)). - person William Robertson; 13.03.2021
comment
Уильям, как получить одно значение за раз, если я открою курсор один раз и поставлю условие, скажем, где id=l_names; Итерация идет не так, как ожидалось, можете ли вы предоставить фрагмент кода? - person Linnea; 13.03.2021
comment
Я не понимаю, что вы спрашиваете. Если вы хотите вернуть курсор, он уже указывает набор строк. Курсор в моем примере дает 3 строки, что соответствует ожидаемому результату в вашем вопросе. Что такое одно значение за раз? Вы не можете вернуть набор курсоров, каждый из которых содержит одну строку, если вы это имеете в виду. - person William Robertson; 13.03.2021
comment
нет-нет, я просто хочу перебрать все три записи, хранящиеся в курсоре, с помощью запроса выбора, как закодировано в моей процедуре, и вывести его через SYS_REFCURSOR - person Linnea; 13.03.2021
comment
Выводить что хоть? rc — это курсор ссылки. Он представляет собой набор из нуля или более строк. Вы возвращаете один ref-курсор, rc. Вы должны открыть его из запроса, вы не можете построить его постепенно. При чем здесь итерация? - person William Robertson; 13.03.2021

Для чего нужен курсор? Как и параметр процедуры IN (так как вы его никогда не использовали)?

В любом случае:

create or replace procedure myproc (i_id in varchar2, rc out sys_refcursor)
  as
begin
  open rc
    select t.id,
           t.country,
           t.account 
    from table2 t join table1 a on a.id = t.id
    where a.currency = 'GBP';
end;
person Littlefoot    schedule 12.03.2021
comment
Хорошо, но, как вы видели, это просто не сработает. RC вернул первый идентификатор, возвращенный в вашем L_NAMES, и все. Неважно, сколько таблиц вам нужно объединить, Oracle может это сделать. - person Littlefoot; 12.03.2021
comment
Если вам нужно сделать это таким образом, сохраните возвращаемые значения в коллекцию - добавляйте по частям по мере выполнения кода - и возвращайте ее (коллекцию) вместо refcursor. - person Littlefoot; 12.03.2021
comment
Но вы уже это сделали, L_NAMES — это коллекция. Создайте один (на уровне SQL), который будет содержать результат, который вы хотите вернуть. - person Littlefoot; 12.03.2021
comment
Я имел в виду CREATE TYPE на уровне SQL, а не внутри процедуры, иначе вы не сможете использовать результат этой процедуры, а это то, что вы планируете сделать, верно? Почему? Потому что вы ВОЗВРАЩАЕТЕ результат процедуры! - person Littlefoot; 12.03.2021
comment
Предоставленное решение не отвечает на мой вопрос, если вы посмотрите на предоставленный код, это будет полезно. - person Linnea; 13.03.2021