2016-02-19 92 views
1

我的plsql代碼出現問題,嘗試幾乎所有操作。現在我放鬆我的思想和力量來解決我的問題:)在Oracle中生成動態SQL

這種情況是,我想搜索所有表架構的特定字符串分配給變量v_ss並打印到DBMS_OUTPUT。我知道這種情況下有現成的解決方案,但我想自己編寫代碼。下面的代碼在我的v_stmt中給了我一個「表不存在」的錯誤。我假設這個select不能識別rec.column_name,但是爲什麼?

這裏是我的代碼:

DECLARE 
v_stmt VARCHAR2(1000); 
v_ss VARCHAR2(30) := 'Argentina'; 

v_own ALL_TAB_COLUMNS.OWNER%TYPE; 
v_tab_nam ALL_TAB_COLUMNS.TABLE_NAME%TYPE; 
v_col_nam ALL_TAB_COLUMNS.COLUMN_NAME%TYPE; 

CURSOR cur_asc IS 
    SELECT t.owner, t.table_name, t.column_name 
    FROM SYS.ALL_TAB_COLUMNS t 
    WHERE t.OWNER LIKE 'HR' 
    AND t.DATA_TYPE LIKE 'VARCHAR2'; 


BEGIN 
    FOR rec IN cur_asc LOOP 
    v_stmt := 'SELECT rec.owner, rec.table_name, rec.column_name FROM rec.table_name WHERE rec.column_name LIKE :1'; 
    EXECUTE IMMEDIATE v_stmt INTO v_own, v_tab_nam, v_col_nam USING v_ss; 
    DBMS_OUTPUT.put_line(v_own || ':' || v_tab_nam || ':' || v_col_nam); 
    END LOOP; 


END; 
/

Error report - 
ORA-00942: table or view does not exist 
ORA-06512: at line 19 
00942. 00000 - "table or view does not exist" 
*Cause:  
*Action: 

你能不能給我一個解釋爲我的錯誤,以及如何解決它?在此先感謝

回答

1

動態語句在一個上下文中執行看不到您的PL/SQL變量,所以當它運行rec.table_name等被解釋爲SQL級別的對象 - 不存在。

您必須將變量值連接到fromwhere子句的動態語句中;你可以做同樣的選擇列表(雖然他們會需要被包裹在轉義單引號,因爲它們都是字符串),或使用綁定變量有:

v_stmt := 'SELECT :owner, :table_name, :column_name FROM ' 
    || rec.table_name || ' WHERE ' || rec.column_name || ' LIKE :ss'; 
EXECUTE IMMEDIATE v_stmt INTO v_own, v_tab_nam, v_col_nam 
USING rec.owner, rec.table_name, rec.column_name, v_ss; 

不能使用綁定變量對象標識符,因此需要這些部分的連接。

除非你運行此作爲HR用戶,在這種情況下,你可能會使用user_tables代替all_tables,你還需要在查詢中指定的模式(由於託尼和拉利提及);無論是硬編碼HR或使用該查詢老闆:

v_stmt := 'SELECT :owner, :table_name, :column_name FROM ' 
    || rec.owner || '.' || rec.table_name 
    || ' WHERE ' || rec.column_name || ' LIKE :ss'; 

這將錯誤不包含正好一個匹配值雖然所有表 - 如果動態選擇得到了零行,或者一個以上行。但這是一個單獨的問題。

1

有2個問題在這裏:

  1. 您需要將表和列名連接成動態SQL,因爲「SELECT rec.owner ......」正試圖選擇一列從表中被稱爲owner別名爲rec,它不會引用您的for循環記錄。
  2. 由於表格中可能沒有匹配的行或許多匹配的行,因此您無法使用select into,因此需要使用遊標。

嘗試:

DECLARE 
v_stmt VARCHAR2(1000); 
v_ss VARCHAR2(30) := 'Argentina'; 

v_own ALL_TAB_COLUMNS.OWNER%TYPE; 
v_tab_nam ALL_TAB_COLUMNS.TABLE_NAME%TYPE; 
v_col_nam ALL_TAB_COLUMNS.COLUMN_NAME%TYPE; 

CURSOR cur_asc IS 
    SELECT t.owner, t.table_name, t.column_name 
    FROM ALL_TAB_COLUMNS t 
    WHERE t.DATA_TYPE LIKE 'VARCHAR2' 
    AND ROWNUM < 10; 

c SYS_REFCURSOR; 

BEGIN 
    FOR rec IN cur_asc LOOP 
    v_stmt := 'SELECT ''' || rec.owner || ''',''' || rec.table_name || ''', ''' || rec.column_name || ''' FROM ' || rec.table_name || ' WHERE ' || rec.column_name || ' LIKE :1'; 
    OPEN c FOR v_stmt USING v_ss; 
    LOOP 
     FETCH c INTO v_own, v_tab_nam, v_col_nam; 
     EXIT WHEN c%NOTFOUND; 
     DBMS_OUTPUT.put_line(v_own || ':' || v_tab_nam || ':' || v_col_nam); 
    END LOOP; 
    CLOSE c; 
    END LOOP; 
END; 
/
+0

你有一個錯字,因爲'USER_'視圖沒有所有者列。 –

+0

@Lalit,謝謝 - 糾正你的錯誤。 –

1

v_stmt:= 'SELECT rec.owner,rec.table_name,rec.column_name FROM rec.table_name WHERE rec.column_name LIKE:1';

  1. 您的動態sql語句格式錯誤。如果將單引號之間的變量括起來,那麼它們將被視爲文字而不再是變量。
  2. 您必須在表名之前加上模式的前綴,否則在以HR用戶身份連接時必須運行腳本。
 
v_stmt := 'SELECT '''||rec.owner||''', '''|| rec.table_name||''', ' 
        ||rec.column_name||' FROM '||rec.owner||'.' 
        ||rec.table_name||' WHERE '||rec.column_name||' LIKE :1'; 

一定要記住,無論何時使用動態報表工作時,總是使用DBMS_OUTPUT首先驗證所產生的實際的SQL。這是調試動態查詢的最佳方法。

  • 您需要處理NO_DATA_FOUND異常,因爲它會引發錯誤的所有那些沒有任何匹配您正在使用的過濾表。
  •  
    SQL> DECLARE 
        2 v_stmt VARCHAR2(1000); 
        3 v_ss VARCHAR2(30) := 'Argentina'; 
        4 v_own ALL_TAB_COLUMNS.OWNER%TYPE; 
        5 v_tab_nam ALL_TAB_COLUMNS.TABLE_NAME%TYPE; 
        6 v_col_nam ALL_TAB_COLUMNS.COLUMN_NAME%TYPE; 
        7 CURSOR cur_asc 
        8 IS 
        9  SELECT t.owner, 
    10  t.table_name, 
    11  t.column_name 
    12  FROM SYS.ALL_TAB_COLUMNS t 
    13  WHERE t.OWNER LIKE 'HR' 
    14  AND t.DATA_TYPE LIKE 'VARCHAR2'; 
    15 BEGIN 
    16 FOR rec IN cur_asc 
    17 LOOP 
    18  v_stmt := 'SELECT '''||rec.owner||''', '''|| rec.table_name||''', ' 
    19       || rec.column_name||' FROM '||rec.owner||'.' 
    20       || rec.table_name||' WHERE '||rec.column_name||' LIKE :1'; 
    21  BEGIN 
    22  EXECUTE IMMEDIATE v_stmt INTO v_own, 
    23  v_tab_nam, 
    24  v_col_nam USING v_ss; 
    25  DBMS_OUTPUT.put_line(v_own || ':' || v_tab_nam || ':' || v_col_nam); 
    26  EXCEPTION 
    27  WHEN NO_DATA_FOUND THEN 
    28  NULL; 
    29  END; 
    30  END LOOP; 
    31 END; 
    32 /
    HR:COUNTRIES:Argentina 
    
    PL/SQL procedure successfully completed. 
    
    +1

    也許需要處理too_many_rows呢?如果v_ss具有通配符,則可能具有不同但使用'like'可能意味着仍可能有多個匹配。 –

    +0

    也許不適用於沒有任何操作/重複的標準HR模式。但總的來說是,需要處理。似乎OP是爲了某種學習目的而做的,否則這種方法應該完全避免。 –