简体   繁体   English

为什么立即执行会引发此PLS-00201错误?

[英]Why does Execute Immediate throw this PLS-00201 error?

I was trying to run the following code and it failed in the execute immediate block. 我试图运行以下代码,但在立即执行代码块中失败。 So, am I going wrong with the syntax? 所以,我语法有问题吗?

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; 

I get this error 我得到这个错误

PLS-00201: Identifier EMP_REC.EMP_NO must be declared PLS-00201:必须声明标识符EMP_REC.EMP_NO

Your emp_rec variable is a local PL/SQL record. 您的emp_rec变量是本地PL / SQL记录。 When you do this, even with a static field name reference: 当您执行此操作时,即使使用静态字段名称引用也是如此:

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

the dynamic SQL runs in a separate context to the block that calls it. 动态SQL在调用它的块的单独上下文中运行。 You then run a new anonymous PL/SQL block within that context. 然后,您在该上下文中运行一个新的匿名PL / SQL块。

Any variables from your outer anonymous block, specifically emp_rec here, are out of scope to the dynamic SQL context. 外部匿名块中的任何变量(这里特别是emp_rec )都超出了动态SQL上下文的范围。 They just do not exist to the code that is trying to assign the value to :x . 它们只是不存在于试图将值分配给:x的代码中。

You could possibly do something with dbms_sql to make this dynamic, but if you know the table columns it would be easier to do: 您可以使用dbms_sql进行某些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; 
/

although as @APC pointed out, the loop is now a bit pointless, since you can just do: 尽管正如@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; 
/

The emp_rec in the EXECUTE IMMEDIATE statement exists in a different namespace from the emp_rec in the calling code. emp_rec在EXECUTE IMMEDIATE语句存在于从不同的命名空间emp_rec调用代码。

Not sure exactly what you're trying to achieve but it might be something like this: 不确定确切要达到的目标,但是可能是这样的:

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; 

Note: I tested a version of this code in 12C and it does work there. 注意:我在12C中测试了此代码的版本,并且在该版本中可以正常工作。 Alas it doesn't work in 11gR2 (and presumably earlier versions too); las,它不适用于11gR2(可能也是早期版本); it hurls PLS-00457 . 它抛出PLS-00457 Still, 11gR2 is pretty much out of support except for folks with deep pockets, everybody ought to be using 12c by now:) 尽管如此,除了财力雄厚的人以外,11gR2几乎没有支持,现在每个人都应该使用12c :)

My guess would be that you have a hidden column in your emp table. 我的猜测是您的emp表中有一个隐藏的列。 Those are columns that've been marked as unused but not yet dropped, and as such, they are not available to be selected from. 这些列已被标记为未使用但尚未删除,因此不能从中选择。

You could update your cursor to use: 您可以更新光标以使用:

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

or: 要么:

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

Note the different view name used in both queries - user_tab_columns doesn't output rows for hidden columns, whereas user_tab_cols does so you have to explicitly filter them out if you don't want to see them. 请注意两个查询中使用的视图名称不同-user_tab_columns不会为隐藏列输出行,而user_tab_cols会输出,因此如果您不想看到它们,则必须明确地将其过滤掉。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM