为什么要立即执行此PLS-00201错误



我试图运行以下代码,并且在Execute即时块中失败。所以,语法我会出错吗?

DECLARE
  l_data long; 
  emp_rec EMP%rowtype;
begin 
  select *  INTO emp_rec  from EMP A WHERE A.EMP_NO = '001322';
 for x in ( select column_name, data_type 
            from user_tab_columns 
             where table_name = 'EMP' ) 
 loop 
   execute immediate 
      'begin 
        :x := emp_rec.' || x.column_name || '; 
      end;' using OUT l_data; 
     dbms_output.put_line( x.column_name || ' = ' || l_data ); 
  end loop; 
end; 

我得到此错误

pls-00201:必须声明标识符emp_rec.emp_no

您的emp_rec变量是本地PL/SQL记录。当您执行此操作时,即使使用静态字段名称参考:

 execute immediate 'begin :x := emp_rec.emp_no; end;' 

动态SQL在调用它的块的单独上下文中运行。然后,您在该上下文中运行一个新的匿名PL/SQL块。

来自外部匿名块,特别是emp_rec的任何变量都不范围与动态SQL上下文。它们只是不存在试图将值分配给:x的代码。

您可能会使用dbms_sql做一些动态的事情,但是如果您知道表列,则更容易执行:

declare
  l_data varchar2(4000); -- long is deprecated; how big does this really need to be?
  emp_rec EMP%rowtype;
begin 
  select *  INTO emp_rec  from EMP A WHERE A.EMP_NO = '001322';
  for x in (
    select column_name, data_type 
    from user_tab_columns 
    where table_name = 'EMP'
  ) 
  loop
    case x.column_name
      when 'EMP_NO' then
        l_data := emp_rec.emp_no;
      -- when clauses for each column in your real table
      when 'FIRST_NAME' then
        l_data := emp_rec.first_name;
      when 'LAST_NAME' then
        l_data := emp_rec.last_name;
      -- list other columns and assignments
      -- else ...
    end case;
    dbms_output.put_line( x.column_name || ' = ' || l_data ); 
  end loop; 
end; 
/

尽管正如@APC指出的那样,循环现在有点毫无意义,因为您可以做:

declare
  emp_rec EMP%rowtype;
begin 
  select *  INTO emp_rec  from EMP A WHERE A.EMP_NO = '001322';
  dbms_output.put_line( 'EMP_NO = ' || emp_rec.emp_no ); 
  dbms_output.put_line( 'FIRST_NAME = ' || emp_rec.first_anme ); 
  dbms_output.put_line( 'LAST_NAME = ' || emp_rec.last_name ); 
  -- ... any other columns you want to show
end; 
/

execute立即语句中的 emp_rec与调用代码中的emp_rec不同的名称空间存在。

不确定您要实现的目标,但可能是这样的:

DECLARE
     l_data long; 
     emp_rec EMP%rowtype;
begin 
      select *  INTO emp_rec  from EMP A WHERE A.EMP_NO = '001322';
     for x in ( select column_name, data_type 
            from user_tab_columns 
            where table_name = 'EMP' ) 
     loop 
         execute immediate 
         'declare
           lrec EMP%rowtype;
         begin 
            lrec := :emp_rec;
             :x := lrec.' || x.column_name || '; 
         end;' using  emp_rec, OUT l_data; 
         dbms_output.put_line( x.column_name || ' = ' || l_data ); 
     end loop; 
end; 

注意:我在12C中测试了此代码的一个版本,并且在那里确实有效。las,它在11gr2中不起作用(大概也是更早的版本);它投掷PLS-00457。尽管如此,除了有深度口袋的人以外,11gr2几乎是不支持的,每个人现在都应该使用12c:)

我的猜测是您在EMP表中有一个隐藏的列。这些是被标记为未使用但尚未下降的列,因此,它们无法从中选择。

您可以更新光标以使用:

 select column_name, data_type 
 from   user_tab_cols 
 where  table_name = 'EMP'
 and    hidden_column != 'NO'

或:

 select column_name, data_type 
 from   user_tab_columns 
 where  table_name = 'EMP';

注意两个查询中使用的不同视图名称-USER_TAB_COLUMNS未输出隐藏列的行,而User_tab_cols则必须明确地将它们明确过滤出来。

最新更新