【问题标题】:PL/SQL - Return cursor that selects data from loopPL/SQL - 返回从循环中选择数据的游标
【发布时间】:2019-11-12 16:48:42
【问题描述】:

我对 PL/SQL 有以下问题:

我正在尝试为 Oracle Cloud Control Metric Extension 编写 PL/SQL 语句。 Metric 想要一个 CURSOR 作为结果:

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_idtbspname 中的最后一个值。

但是如何让光标选择所有行? 我不想创建一个表来存储值。

以下适用于 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

我想要与代码中光标返回的第二个示例相同的结果?

我认为循环是不可替代的,因为稍后将为每个表空间处理更多数据。

【问题讨论】:

  • 您是否有理由不想只为将v$tablespace 连接到dba_tablespaces 的查询打开游标?您是否真的在循环中进行了未在此处显示但在单个 SQL 语句中不起作用的额外处理?
  • 还有一些其他变量会被其他一些表中的数据填充。结果将包含比数字和表空间名称更多的数据。光标应选择大约 10 个变量。我只想让核心工作或知道是否有可能以这种方式。
  • 但是您可以将这些表添加为附加连接吗?如果你从一个刚加入表的核心开始,可以吗?或者你真的需要一个包含循环的核心,这些循环是你自己的连接吗?您可以创建一个对象,然后创建一个集合,将它们填充到您的匿名 PL/SQL 块中,然后打开一个从该集合中选择的游标。但这将涉及在数据库中创建新类型(这可能是权限问题),并且可能会使代码比连接更长更复杂。
  • 是的,存在权限问题。我已经试过了。我会看看我是否可以加入所有表格并发布更新。谢谢

标签: sql database oracle plsql cloudcontrol


【解决方案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;

【讨论】:

  • 第二个方向,我认为它应该可以工作,但正如预期的那样,我得到了 ORA-01031:权限不足。连接可能是可能的,但会非常复杂,因为我从“dba_hist_tbspc_space_usage”和“dba_tablespaces”中为每个表空间收集一些数据并将这些值存储在变量中。此外,我对变量进行了一些算术运算。每个变量都已被多个位置的连接填充。
  • @unixora - 在单个 SQL 语句中执行这一切听起来很合理 - 您只是在表空间名称上将一堆表连接在一起。哪个语句会引发错误?我猜您必须要求您的 DBA 授予您创建类型的权限。
猜你喜欢
  • 2012-03-27
  • 2012-04-28
  • 1970-01-01
  • 1970-01-01
  • 2023-04-01
  • 1970-01-01
  • 1970-01-01
  • 2014-05-14
  • 2012-05-29
相关资源
最近更新 更多