PL / SQL - курсор возврата, который выбирает данные из цикла

У меня проблема с PL / SQL:

Я пытаюсь написать оператор PL / SQL для расширения метрики Oracle Cloud Control. Метрике нужен КУРСОР в качестве результата:

PL / SQL должен заканчиваться примерно следующим образом

OPEN :1 FOR SELECT var1, var2 FROM DUAL;

Переменная связывания 1 и тип «SQL_CURSOR» определяются приложением.

Мой код выглядит так:

DECLARE
  v_ts_id NUMBER;
  tbspname VARCHAR2(200);

  CURSOR all_tablespaces IS 
    select distinct tablespace_name 
    from dba_tablespaces
    where tablespace_name not in ('UNDO','TEMP');

 BEGIN

   FOR v_ts_name IN all_tablespaces LOOP

     tbspname := UPPER(v_ts_name.tablespace_name);

     SELECT ts# into v_ts_id 
     FROM v$tablespace 
     where name = tbspname;

   END LOOP;

   OPEN :1 FOR SELECT v_ts_id, tbspname FROM DUAL;  
 END;

В результате получается одна строка. Имеет смысл, потому что курсор просто выбирает последние значения, хранящиеся в v_ts_id и tbspname.

Но как заставить курсор выделять все строки? Я не хочу создавать таблицу для хранения значений.

Следующие работы на SQL * Plus

set serverout on
 set verify off
 set lines 200
 set pages 2000

DECLARE
  v_ts_id number;
  tbspname varchar2(200);

  CURSOR all_tablespaces IS 
    select distinct tablespace_name 
    from dba_tablespaces 
    where tablespace_name not in ('UNDO','TEMP');

 BEGIN

 FOR v_ts_name IN all_tablespaces LOOP

   tbspname := UPPER(v_ts_name.tablespace_name);

   SELECT ts# into v_ts_id 
   FROM v$tablespace
   where name = tbspname;

   DBMS_OUTPUT.PUT_LINE(v_ts_id ||','||tbspname);

  END LOOP;

EXCEPTION
  WHEN NO_DATA_FOUND THEN
    DBMS_OUTPUT.PUT_LINE('Tablespace usage information not found in AWR');
END;
/

Часть РЕЗУЛЬТАТА выглядит так:

5,USERS
1,SYSAUX
0,SYSTEM

Я хочу получить тот же результат, что и во втором примере, возвращаемый курсором в моем коде?

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


person unixora    schedule 12.11.2019    source источник
comment
Есть ли причина, по которой вы не хотите просто открывать курсор для запроса, который соединяет v$tablespace с dba_tablespaces? Вы действительно выполняете дополнительную обработку в цикле, который здесь не показан и который не работает с одним оператором SQL?   -  person Justin Cave    schedule 12.11.2019
comment
Есть дополнительные переменные, которые заполняются данными из некоторых других таблиц. В результате будет больше данных, чем просто число и имя табличного пространства. Курсор должен выбрать около 10 переменных. Я просто хочу заставить ядро ​​работать или узнать, возможно ли это вообще.   -  person unixora    schedule 12.11.2019
comment
Но можно ли просто добавить эти таблицы в качестве дополнительных объединений? Если вы начнете с ядра, которое просто присоединяется к таблицам, это нормально? Или вам действительно нужно ядро, включающее циклы, которые выполняют ваши собственные соединения? Вы можете создать объект, а затем коллекцию, заполнить их в анонимном блоке PL / SQL, а затем открыть курсор, который выбирает из этой коллекции. Но это потребует создания новых типов в базе данных (что может быть проблемой с привилегиями) и, вероятно, сделает код намного длиннее и сложнее, чем соединение.   -  person Justin Cave    schedule 12.11.2019
comment
Да, есть проблемы с привилегиями. Я уже пробовал это. Я посмотрю, смогу ли я присоединиться ко всем таблицам и опубликовать обновление. Спасибо   -  person unixora    schedule 12.11.2019


Ответы (1)


Простым подходом было бы просто выполнить соединение

begin
  open :1 
   for select vt.ts#, dt.tablespace_name
         from v$tablespace vt
              join dba_tablespaces dt
                on vt.tablespace_name = dt.tablespace_name
        where dt.tablespace_name not in ('UNDO','TEMP');
end;

Если вам действительно нужно выполнить цикл, должно сработать что-то вроде этого

create type my_ts_obj as object (
  ts# integer,
  tablespace_name varchar2(30)
);

create type my_ts_nt is table of my_ts_obj;

declare
  l_ts my_ts_nt := new my_ts_nt();
  l_ts# integer;
begin
  for dt in (select distinct tablespace_name 
               from dba_tablespaces 
              where tablespace_name not in ('UNDO','TEMP'))
  loop
    select vt.ts#
      into l_ts#
      from v$tablespace vt
     where vt.tablespace_name = dt.tablespace_name;

    l_ts.extend();
    l_ts( l_ts.count ) := new my_ts_obj( l_ts#, dt.tablespace_name );
  end loop;

  open :1
   for select *
         from table( l_ts );
end;
person Justin Cave    schedule 12.11.2019
comment
Второй идет в нужном направлении, и я думаю, что он должен работать, но, как и ожидалось, я получаю ORA-01031: недостаточные привилегии. Соединение возможно, но будет очень сложным, потому что я собираю некоторые данные из dba_hist_tbspc_space_usage и dba_tablespaces для каждого табличного пространства и сохраняю значения в переменных. Кроме того, я проделываю некоторые арифметические операции с переменными. Каждая переменная уже заполнена объединением нескольких позиций. - person unixora; 13.11.2019
comment
@unixora - все это звучит вполне разумно с помощью одного оператора SQL - вы просто объединяете несколько таблиц вместе по имени табличного пространства. Какой оператор вызывает ошибку? Я предполагаю, что вам придется попросить администратора базы данных дать вам разрешение на создание типов. - person Justin Cave; 13.11.2019