2011-10-12 44 views
5

的存在是否有可能具有條件編譯Oracle中,其中所述病症是數據庫對象(具體地,一個表或視圖或同義詞)的存在?我希望能夠做這樣的事情:PL/SQL編譯有條件地對數據庫對象

sp_some_procedure is 
    $IF /*check if A exists.*/ then 
     /* read from and write to A as well as other A-related non-DML stuff...*/ 
    $ELSE /*A doesn't exist yet, so avoid compiler errors*/ 
     dbms_output.put_line('Reminder: ask DBA to create A!') 
    $ENDIF 
end; 
+1

我不認爲這是一個好主意,即使這是可能的。現在,您的代碼將通過編譯,但會在運行時失敗(設計失敗)。這是一個折衷,我不會輕視(但自然是YMMV)。 – user272735

+1

@ user272735:如果我有其他解決方法的代碼,我不會使用它,而不是輸出消息。我有解決方法的代碼,但它從SQL * Plus腳本運行並作爲不同的用戶運行。我同意這是有風險的,我的想法是將其作爲一種臨時措施,以便我可以繼續測試,而不是等待......並等待......爲DBA提供幫助。我以爲我曾經在C中看過類似的技巧,不確定我是否可以在PL/SQL中做類似的事情。 – FrustratedWithFormsDesigner

+2

當你等待時,你可以享受到我的所有同情......並等待着...... DBA協助(我自己也一直在這裏 - 令人沮喪)。如果_waste_長期下降,任何有助於您前進的東西都會很好。我相信你知道這些臨時黑客變得如何輕鬆變成永久黑客。 – user272735

回答

1

沒有 - 那是不可能的......但如果你創建一個存儲過程引用不存在的數據庫對象,並嘗試編譯它編譯會顯示錯誤...存儲過程將是有,但「無效」 ...每當他看到DBA時,編譯錯誤都可以訪問......所以我會繼續創建所有需要的存儲過程,如果出現任何編譯錯誤,請詢問DBA(有時存在對象但存儲過程需要權限來訪問它......)...在錯誤原因修復後,您可以重新編譯存儲過程(通過ALTER PROCEDURE MySchema.MyProcName COMPILE;),並且一切正常...

如果您沒有想要代碼在那裏,你可以只需DROP存儲過程和/或替換是通過CREATE OR REPLACE ...與dbms_output.put_line('Reminder: ask DBA to create A!')在身體。

的唯一選擇就是凱文指出EXECUTE IMMEDIATE適當EXCEPTION處理...

+0

存儲過程不是獨立存在的,它存在於其他人使用的包中,這需要對其他代碼運行有效。非開發人員環境的構建過程由腳本控制,這些腳本會拋出許多醜陋的消息,這些消息因我的*任何*編譯器/ DML/DDL錯誤而退縮。我在想,如果PL/SQL不能這樣做,我最好更加努力地在DBA上創建A.重寫所有我的代碼以使用'立即執行'在這一點上是不值得的暫時的情況。謝謝! – FrustratedWithFormsDesigner

2

我會用「EXECUTE IMMEDIATE」和例外條款。

+0

你的意思是'立即執行'所有代碼的時間是否存在?有趣的,但是有相當多的代碼,而不僅僅是DML。如果可能,我寧願代碼不在那裏。 – FrustratedWithFormsDesigner

2

使用動態SQL創建的軟件包常數來跟蹤哪些對象存在,然後在條件編譯使用這些常量。

--E.g., say there are two possible tables, but only one of them exists. 
--create table table1(a number); 
create table table2(a number); 


--Create a package with boolean constants to track the objects. 
--(Another way to do this is to use ALTER SESSION SET PLSQL_CCFLAGS) 
declare 
    table1_exists_string varchar2(10) := 'true'; 
    table2_exists_string varchar2(10) := 'true'; 
    temp number; 
begin 
    begin 
    execute immediate 'select max(1) from table1 where rownum <= 1' into temp; 
    exception when others then 
    table1_exists_string := 'false'; 
    end; 

    begin 
    execute immediate 'select max(1) from table2 where rownum <= 1' into temp; 
    exception when others then 
    table2_exists_string := 'false'; 
    end; 

    execute immediate ' 
    create or replace package objects is 
     table1_exists constant boolean := '||table1_exists_string||'; 
     table2_exists constant boolean := '||table2_exists_string||'; 
    end; 
    '; 
end; 
/

--Look at the results in the source: 
select * from user_source where name = 'OBJECTS'; 


--Create the object that refers to the tables. 
create or replace function compile_test return varchar2 is 
    v_test number; 
begin 
    $if objects.table1_exists $then 
     select max(1) into v_test from table1; 
     return 'table1 exists'; 
    $elsif objects.table2_exists $then 
     select max(1) into v_test from table2; 
     return 'table 2 exists'; 
    $else 
    return 'neither table exists'; 
    $end 
end; 
/

--Check the dependencies - only TABLE2 is dependent. 
select * from user_dependencies where name = 'COMPILE_TEST'; 

--Returns 'table 2 exists'. 
select compile_test from dual; 

混合動態SQL,動態PL/SQL,和條件編譯通常是一個很邪惡的想法。但它可以讓你將所有醜陋的動態SQL放在一個安裝包中,並保持真正的依賴關係跟蹤。

這可能適用於半動態環境;例如一個安裝有不同對象集的程序,但是它們之間不會頻繁變化。如果你的系統失敗了,失敗應該是顯而易見的,所以它可以用立即修復大多數人忽略任何開頭「提醒...」)

+0

*非常*非常聰明,非常邪惡! > :)環境不是*應該*是這種動態,但它是最後幾天... – FrustratedWithFormsDesigner

0

我會做的是通過ALL_OBJECTS檢查存在,是這樣的:。

declare 

l_check_sql varchar2(4000); 
l_cnt number; 

begin 

l_check_sql := q'{ 
select count(1) 
from all_objects 
where object_name = 'MY_OBJ' 
and owner = 'MY_OWNER' 
}'; 

execute immediate l_check_sql into l_cnt; 

if (l_cnt > 0) then 
    -- do something referring to MY_OBJ 
else 
    -- don't refer to MY_OBJ 
end if; 

end; 
3

是的。這裏是第一個存儲過程希望從XALL_TABLES中選擇的示例,但如果該表不存在,則從雙重選擇。最後,因爲我沒有XALL_TABLES對象,所以第一個存儲過程從雙重選擇。第二個,在ALL_TABLES對象上做同樣的事情。因爲ALL_TABLES存在,所以第二個存儲過程從all_tables中選擇,而不是從DUAL中選擇。

這樣的結構,其中包必須被部署在未部署無處不在的所有數據庫和使用表...(好吧,也許有一個概念性的問題,但它發生)是有用的。

--conditionals compilation instructions accept only static condition (just with constants) 
--passing sql bind variable doesn't work 
--To pass a value to a conditional compilation instruction, I bypasses the use of input parameters of the script 
--these 4 next lines affect a value to the first and the second input parameter of the script 
--If your originally script use input script parameter, use the next free parameter ... 
column param_1 new_value 1 noprint 
select nvl(max(1), 0) param_1 from all_views where owner = 'SYS' and view_name = 'XALL_TABLES'; 
column param_2 new_value 2 noprint 
select nvl(max(1), 0) param_2 from all_views where owner = 'SYS' and view_name = 'ALL_TABLES'; 

CREATE or replace PACKAGE my_pkg AS 
    function test_xall_tables return varchar2; 
    function test_all_tables return varchar2; 
END my_pkg; 
/

CREATE or replace PACKAGE BODY my_pkg AS 

    function test_xall_tables return varchar2 is 
    vch varchar2(50); 
    begin 
    $IF (&1 = 0) $THEN 
     select 'VIEW XALL_TABLES D''ONT EXISTS' into vch from dual; 
    $ELSE 
     select max('VIEW XALL_TABLES EXISTS') into vch from XALL_TABLES; 
    $END   
    return vch;  
    end test_xall_tables; 

    function test_all_tables return varchar2 is 
    vch varchar2(50); 
    begin 
    $IF (&2 = 0) $THEN 
     select 'VIEW ALL_TABLES D''ONT EXISTS' into vch from dual; 
    $ELSE 
     select max('VIEW ALL_TABLES EXISTS') into vch from ALL_TABLES; 
    $END 
    return vch; 
    end test_all_tables;    
END my_pkg; 
/

測試:

select my_pkg.test_xall_tables from dual; 

VIEW XALL_TABLES D'ONT EXISTS

select my_pkg.test_all_tables from dual; 

VIEW ALL_TABLES EXISTS