2017-03-09 54 views
0

我試圖運行下面的代碼,它在執行即時塊中失敗。那麼,我的語法錯了嗎?爲什麼Execute立即拋出PLS-00201錯誤?

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

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

如果我們要在循環體中對列名進行硬編碼,幾乎不需要查詢USER_TAB_COLUMNS在所有 – APC

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如果你不想看到它們,你必須明確地將它們過濾掉。

+0

錯誤來自動態SQL;並引用'emp_no',這也是在遊標中使用 - 所以會得到一個編譯錯誤,我認爲從光標的ORA-00904,而不是從動態SQL的PLS-00201,如果它被隱藏? –

+0

@AlexPoole是的,我看到了其他答案(看起來他們是正確的!),但我認爲我會留下我的答案,因爲無論如何隱藏的列都需要正確處理。 – Boneist

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 :)

+0

你能像這樣使用一個綁定變量的記錄 - 是12c中的新變量嗎? –

+0

啊好點。我會更新我的答案。 – APC

相關問題