2011-10-12 16 views
5

¿Es posible tener una compilación condicional en Oracle, donde la condición es la existencia de un objeto de base de datos (específicamente, una tabla o vista o sinónimo)? Me gustaría ser capaz de hacer algo como esto:PL/SQL compila condicionalmente la existencia del objeto de base de datos

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

No creo que sea una buena idea, incluso si hubiera sido posible. Ahora su código pasará la compilación pero fallará (está diseñado para fallar) en tiempo de ejecución. Esa es una compensación que no tomaría a la ligera (pero, naturalmente, YMMV). – user272735

+1

@ user272735: No necesariamente tiene que fallar, si tuviera otro código de trabajo, lo habría usado en lugar del mensaje de salida. Tengo un código de solución, pero se ejecuta desde un script SQL * Plus y como un usuario diferente. Estoy de acuerdo en que es arriesgado, mi idea era usarlo como una medida temporal para poder continuar con las pruebas, en lugar de esperar ... y esperar ... para recibir asistencia del DBA. Pensé que había visto trucos similares una vez en C, no estaba seguro de si podría hacer algo similar en PL/SQL. – FrustratedWithFormsDesigner

+2

Tiene toda mi simpatía que puede disfrutar cuando está esperando ... y esperando ... Asistencia de DBA (he estado allí también, frustrante). Todo lo que te ayude a avanzar es bueno si _waste_ se reduce a largo plazo. Estoy seguro de que sabes cuán fácilmente esos hacks temporales se convierten en permanentes. – user272735

Respuesta

1

No, eso no es posible ... pero si crea un procedimiento almacenado haciendo referencia a un objeto DB no existente e intenta compilarlo, la compilación mostrará errores ... el procedimiento almacenado estará allí pero "no válido" ... y los errores de compilación son accesibles para el DBA cada vez que lo mira ... así que seguiría adelante y crearía todos los procedimientos almacenados necesarios, si surge algún error de compilación pregunte al DBA (a veces el objeto existe pero el procedimiento almacenado) necesita permisos para acceder ...) ... una vez que se haya solucionado el error, puede simplemente recompilar el procedimiento almacenado (a través de ALTER PROCEDURE MySchema.MyProcName COMPILE;) y todo está bien ...

SI no lo hace quiere que el código esté allí, puede simplemente DROP el procedimiento estropeado y/o reemplazar es a través de CREATE OR REPLACE ... con dbms_output.put_line('Reminder: ask DBA to create A!') en el cuerpo.

La única otra alternativa es como señala Kevin cabo EXECUTE IMMEDIATE con el manejo adecuado EXCEPTION ...

+0

El procedimiento almacenado no vive solo, está dentro de un paquete utilizado por otros, que debe ser válido para que se ejecute otro código. El proceso de compilación para entornos que no son desarrolladores está controlado por scripts que arrojan un montón de mensajes desagradables que me vuelven a bajar para * cualquier * compilador/error DML/DDL. Estoy pensando que si PL/SQL no puede hacer esto, será mejor que presione más sobre el DBA para crear A. Volver a escribir todo mi código para usar 'Execute immediate' no vale la pena en este punto para una situación temporal. ¡Gracias! – FrustratedWithFormsDesigner

2

yo usaría 'EXECUTE IMMEDIATE' y una cláusula de excepción.

+0

¿Quiere decir 'ejecutar inmediatamente' para todo el código cuando A existe? Interesante, pero hay bastante código, no solo DML. Prefiero que el código simplemente no esté allí, si es posible. – FrustratedWithFormsDesigner

2

uso de SQL dinámico para crear constantes del paquete para realizar un seguimiento de qué objetos existen, y luego usar esas constantes en la compilación condicional.

--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; 

Mezclar SQL dinámico, PL/SQL dinámico, y la compilación condicional es generalmente una idea muy mala. Pero le permitirá poner todo su desagradable SQL dinámico en un paquete de instalación y mantener un seguimiento real de la dependencia.

Esto puede funcionar bien en un entorno semidinámico; por ejemplo, un programa que se instala con diferentes conjuntos de objetos pero que no cambia con frecuencia entre ellos.

(Además, si el objetivo de todo esto es simplemente reemplazar mensajes de error de miedo con advertencias amistosas, en mi opinión esa es una muy mala idea. Si su sistema va a fallar, la falla debería ser obvia para que pueda se fijaron inmediatamente mayoría de la gente ignora cualquier cosa que empieza con "recordatorio ...")

+0

* ¡Muy * inteligente y muy malvado! > :) No se supone que el entorno * sea tan dinámico, pero son los últimos días ... – FrustratedWithFormsDesigner

0

lo que quiero hacer es comprobar la existencia a través de all_objects, algo así como:..

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

sí lo es. Aquí una muestra donde el primer procedimiento almacenado quiere seleccionar de XALL_TABLES, pero si esta tabla no existe, seleccione de dual. Finalmente, como no tengo un objeto XALL_TABLES, el primer procedimiento almacenado selecciona desde dual. El segundo, hace lo mismo en el objeto ALL_TABLES. Como ALL_TABLES existe, el segundo procedimiento almacenado selecciona de all_tables pero no de DUAL.

Este tipo de construcción es útil cuando el paquete tiene que implementarse en toda su base de datos y usar tablas que no se implementan en todas partes ... (vale, tal vez haya un problema conceptual, pero sucede).

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

la prueba:

select my_pkg.test_xall_tables from dual; 

dan

VISTA XALL_TABLES D'ONT EXISTE

select my_pkg.test_all_tables from dual; 

dan

VISTA ALL_TABLES EXISTE

Cuestiones relacionadas