2012-01-18 40 views
10

Estoy tratando de hacer algunas comprobaciones en un esquema de base de datos al comienzo de una secuencia de comandos PL/SQL.PL/SQL: ¿hay alguna instrucción para detener completamente la ejecución del script?

Si las comprobaciones dan resultados infructuosos, quiero detener la secuencia de comandos para evitar que se ejecuten las siguientes instrucciones.

que tiene algo como esto

-- 1st line of PL/SQL script 

DECLARE 
    SOME_COUNT INTEGER; 
BEGIN 
    SELECT COUNT(*) INTO SOME_COUNT FROM SOME_TABLE WHERE <SOME_CONDITIONS>; 
    IF (SOME_COUNT > 0) THEN 
    DBMS_OUTPUT.PUT_LINE('Test failed, I don''want the rest of the script' 
     || ' to be executed.'); 
    --EXIT or something like that?... <= STOP EXECUTION HERE 
    END IF; 
END; 
/

-- OTHER SQL INSTRUCTIONS... 
ALTER TABLE SOME_TABLE ... 

Busco la instrucción (s) que permite hacer el "STOP EXECUTION HERE".

+5

¿Estás hablando realmente de un script PL/SQL? ¿O una secuencia de comandos SQL * Plus? No se puede tener una instrucción DDL en PL/SQL (a menos que la coloque en un 'EJECUTAR INMEDIATO'). Entonces me parece que estás hablando de un script SQL * Plus. Si está hablando de un script SQL * Plus, si el bloque PL/SQL se equivoca, SQL * Plus continuará ejecutando de forma predeterminada la siguiente instrucción SQL (o bloque PL/SQL) en el script. Debería usar el comando SQL * Plus 'WHENEVER SQLERROR EXIT'. –

+0

Gracias por tu comentario Justin. De hecho, no soy un "experto en Oracle", incluso si tengo un buen conocimiento de DBMS en general. Por lo tanto, todavía no entendía la diferencia entre los scripts PL/SQL o SQL * Plus (¿conoces algún blog/doc/sitio que explique claramente esa diferencia?). Todo lo que puedo decir es que estoy usando ** Navicat **, usando el panel de "consulta" y cargando el archivo de mi script en él. En ese contexto específico, 'RAISE_APPLICATION_ERROR()' hace el trabajo. –

Respuesta

9

Basado en la pregunta, no estoy de acuerdo con la respuesta aceptada. La pregunta muestra un script por lotes de varias declaraciones. RAISE_APPLICATION_ERROR() solo sale de un bloque PL/SQL (subprograma), no fuera del guión general (como lo señala Justin), por lo que continuará con las afirmaciones que siguen.

Para las secuencias de comandos por lotes, es mejor utilizar WHENEVER SQLERROR EXIT. Sí, es una directiva SQL * Plus, no SQL estándar, pero es bastante portátil; las herramientas de Oracle más populares que admiten scripts admiten esta directiva, al menos parcialmente. El siguiente ejemplo funciona en SQL * Plus, SQL * Developer, Toad, SQLsmith y posiblemente otros, y demuestra el problema, si comenta la línea.

set serveroutput on 

-- Without this line, things keep going 
WHENEVER SQLERROR EXIT SQL.SQLCODE ROLLBACK; 

BEGIN 
    IF (1 > 0) THEN 
    DBMS_OUTPUT.PUT_LINE('First thing'); 
    RAISE_APPLICATION_ERROR(-20000, 'Test failed'); -- not enough 
    END IF; 
END; 
/

-- This will execute if you remove WHEN SQLERROR.., so RAISE_APPLICATION_ERROR is not enough 
BEGIN 
    DBMS_OUTPUT.PUT_LINE('Second thing - Executes anyway'); 
END; 
/

Si extrae la CUANDO SQLERROR, el guión va a continuar y ejecutar el segundo bloque, etc., que es exactamente lo que la pregunta se refiere a evitar.

La ventaja, en este caso, de las herramientas gráficas que emulan sqlplus, es que realmente detienen el script y no envían el resto del script al shell del comando como comandos de shell, que es lo que sucede si pega scripts en SQL * Plus que se ejecutan en una ventana de consola. SQL * Plus puede salir en caso de error, pero los comandos almacenados restantes serán manejados por el shell del sistema operativo, que es un poco desordenado y potencialmente riesgoso, si tiene comandos de shell en los comentarios (que no es desconocido). Con SQLPlus, siempre es mejor conectarse, y luego ejecutar el script, o pasarlo en el argumento < start> command line (sqlplus scott/tiger @ foo.sql) para evitar esto.

+0

Gracias por su respuesta. –

+0

+1 esto realmente ayudó. Sabía sobre excepciones y errores de aplicación, pero necesito hacer una comprobación de condición previa en un script DDL que debería detener el script. La alternativa (hacer DDL en una instrucción IF en una secuencia de comandos PL/SQL) no es muy tentadora. –

5

unos segundos más de google me dio la respuesta: la función RAISE_APPLICATION_ERROR()

IF (SOME_COUNT > 0) THEN 
    RAISE_APPLICATION_ERROR(-20000, 'Test failed'); 
    END IF; 

El código de error definido por el usuario debe estar entre -20000 y -20999.

detalles sobre Oracle doc aquí: http://docs.oracle.com/cd/B10501_01/appdev.920/a96624/07_errs.htm#877 (sección Definición de los mensajes de error propios: Procedimiento RAISE_APPLICATION_ERROR)

+2

Si usa 'raise_application_error' en el código que publicó en su pregunta, lo sacará del bloque PL/SQL pero la sección' OTHER SQL INSTRUCTIONS' se seguirá ejecutando. ¿Es esto lo que buscas? –

7

Si no desea elevar una excepción, usted podría intentar algo así como (no probado):

declare 
    SOME_COUNT INTEGER; 
begin 
    SELECT COUNT(*) INTO SOME_COUNT FROM SOME_TABLE WHERE <SOME_CONDITIONS>; 
    IF (SOME_COUNT > 0) THEN 
    DBMS_OUTPUT.PUT_LINE('Test failed, I don''want the rest of the script' 
     || ' to be executed.'); 

    goto end_proc; 
    END IF; 

    -- A bunch of great code here 

    <<end_proc>> 
    null; -- this could be a commit or other lines of code 
end; 

Algunas personas odian cualquier declaración de GOTO, ya que pueden conducir al código de espagueti si se abusa, pero en situaciones simples como esta (una vez más, suponiendo que no desea hacer una excepción) funcionan bien.

+1

Gracias, +1. El único problema que veo es que tengo un montón de sentencias DDL ('ALTER TABLE' ...) como' montón de código genial', que requieren usar mucho la solución 'EXECUTE IMMEDIATE', por lo tanto, el código del script se ser menos legible/mantenible –

+0

Sí, ese es un problema diferente al de su OP, pero estoy de acuerdo en que algún código está lejos de ser "excelente" ;-) Ese comentario fue un poco irónico, pero una vez más el punto era dar una solución alternativa para su pregunta . – tbone

1

En lugar de arrojar un error de aplicación, es mucho más simple usar el RETURN keyword que sale del bloque PL/SQL actual sin problemas.

¡Solo asegúrese de hacer un DBMS_OUTPUT.PUT_LINE('Exited because <error') antes de que le proporcione al usuario un bonito mensaje de por qué está saliendo, por supuesto!

Cuestiones relacionadas