为什么Execute Immediate会抛出此PLS-00201错误?

时间:2017-03-09 16:07:11

标签: sql oracle plsql dynamic-sql

我试图运行以下代码,但是在执行立即块中失败了。那么,我的语法错了吗?

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

3 个答案:

答案 0 :(得分:3)

您的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; 
/

答案 1 :(得分:2)

EXECUTE IMMEDIATE语句中的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中测试了此代码的一个版本,它在那里工作。唉它在11gR2中不起作用(也可能是早期的版本);它投掷PLS-00457。尽管如此,11gR2几乎没有支持,除了有大笔资金的人,现在每个人都应该使用12c:)

答案 2 :(得分:0)

我的猜测是你的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会这样做,因此如果您不想看到它们,则必须明确过滤它们。