plsql
inicio / programacion / {{page.title}}
{% include toc.html %}
propósito
- notas sobre pl/sql
objetivos
- plantilla de paquete, función, y procedimiento con tratamiento de errores
- una ayuda por pantalla que con el menú genérico muestre una que se llama con el nombre de la pantalla como parámetro de forma que muestra desde base de datos lo que se quiere mostrar en la ayuda
- ver el fallo del
trigger
de aud cuando la tabla esta vacia. - la parte general del paquete plantilla sacarla a otro paquete por ejemplo utilidades.
- llevar estos paquetes a un repositorio privado en bitbucket junto con lo de las gráficas
- entender la inyección de dependencias y ver como aplicarlo a los programas del curro
plantilla de paquete
- en el esquema de la aplicacion base
gab
paquete de proposito general
- general a todos los proyectos SIGI
ref cursor
- los ref cursores
-- DESDE SQL PLUS
SQL> conn gab/desarrollo@xe
Connected.
SQL> SHOW USER
USER is "GAB"
SQL> VAR RC REFCURSOR;
SQL> exec :rc := gab.gab_pck_alumno.f_buscar_alumnos (null,null,1);
PL/SQL procedure successfully completed.
SQL> print rc
GAB_ALUMNO
--------------------------------------------------
GAB_DESCRIPCION
--------------------------------------------------------------------------------
ALUMNO1
DESCRIPCION DEL ALUMNO 1
ALUMNO2
DESCRIPCION DEL ALUMNO 2
ALUMNO3
DESCRIPCION DEL ALUMNO 3
SQL> print rc
SP2-0625: Error printing variable "rc"
SQL>
- el
ref cursor
no contiene los datos resultado de la consulta sino que contiene un puntero a la consulta y por eso el segundoprint
da error - el primer
print
funciona porqueSQL Plus
mira el cursor de referencia y ve que es un cursor abierto pues la sentencia no lo cierra. - Entra en el bucle, obteniendo cada fila de datos de la base de datos, utilizando el cursor de referencia como referencia (puntero) a la consulta relevante, y mostrando cada fila de datos hasta que no tenía más filas para recuperar.
- Una vez que se han obtenido todas las filas, cierra el cursor.
SQL> ed
Wrote file afiedt.buf
1 declare
2 c_alumnos sys_refcursor;
3 begin
4 c_alumnos := gab.gab_pck_alumno.f_buscar_alumnos (null,null,1); -- This returns an open cursor
5 dbms_output.put_line('Alumnos: '||c_alumnos%ROWCOUNT);
6 close c_alumnos;
7* end;
SQL> /
Alumnos: 0
PL/SQL procedure successfully completed.
- sale 0 a pesar de que existen 3 alumnos por que
c_alumnos%ROWCOONT
muestra el número de fila leídas a través del cursos y no hemos leído ninguna.
Wrote file afiedt.buf
1 declare
2 c_alumnos sys_refcursor;
3 v_id_alumno varchar2(50);
4 v_descripcion varchar2(200);
5 begin
6 c_alumnos := gab.gab_pck_alumno.f_buscar_alumnos (null,null,1); -- This returns an open cursor
7 dbms_output.put_line('Pre Fetch: Alumnos: '||c_alumnos%ROWCOUNT);
8 fetch c_alumnos into v_id_alumno, v_descripcion;
9 dbms_output.put_line('Post Fetch: Rows: '||c_alumnos%ROWCOUNT);
10 close c_alumnos;
11* end;
SQL> /
Pre Fetch: Alumnos: 0
Post Fetch: Rows: 1
PL/SQL procedure successfully completed.
- ahora sí que ha leído una fila del cursor.
SQL> ed
Wrote file afiedt.buf
1 declare
2 c_alumnos sys_refcursor;
3 v_id_alumno varchar2(50);
4 v_descripcion varchar2(200);
5 begin
6 c_alumnos := gab.gab_pck_alumno.f_buscar_alumnos (null,null,1); -- This returns an open cursor
7 dbms_output.put_line('Pre Fetch: Alumnos: '||c_alumnos%ROWCOUNT);
8 loop
9 fetch c_alumnos into v_id_alumno, v_descripcion;
10 exit when c_alumnos%NOTFOUND;
11 dbms_output.put_line('Post Fetch: Rows: '||c_alumnos%ROWCOUNT||' '||v_id_alumno||' '||v_descripcion);
12 end loop;
13 close c_alumnos;
14* end;
SQL> /
Pre Fetch: Alumnos: 0
Post Fetch: Rows: 1 ALUMNO1 DESCRIPCION DEL ALUMNO 1
Post Fetch: Rows: 2 ALUMNO2 DESCRIPCION DEL ALUMNO 2
Post Fetch: Rows: 3 ALUMNO3 DESCRIPCION DEL ALUMNO 3
PL/SQL procedure successfully completed.
-- DESDE EPACF
/* Formatted on 18/12/2017 10:12:13 (QP5 v5.287) */
DECLARE
RetVal EPACF.EPACF_PCK_ACTO_CANDIDATO_VAC.cursorActos;
P_ID_CURSO NUMBER;
P_ID_ACTO NUMBER;
P_ID_VACANTE NUMBER;
V_DTP_NIF VARCHAR2( 200 );
V_DTP_NOMBRE VARCHAR2( 200 );
V_DTP_APELLIDO1 VARCHAR2( 200 );
V_DTP_APELLIDO2 VARCHAR2( 200 );
V_DTP_FECHA_NACIMIENTO VARCHAR2( 200 );
V_DTP_TELEFONO VARCHAR2( 200 );
V_DTP_EMAIL VARCHAR2( 200 );
V_ACF_ID_COLECTIVO VARCHAR2( 200 );
V_ACF_DESCRIPCION VARCHAR2( 200 );
V_ACF_TIPO_RESOLUCION VARCHAR2( 200 );
V_ACF_DESC_TIPO_RESOLUCION VARCHAR2( 200 );
V_DTM_GESTOR VARCHAR2( 200 );
V_DTM_DESC_GESTOR VARCHAR2( 200 );
V_ACF_ID_ESTADO VARCHAR2( 200 );
V_ACF_DESC_ESTADO VARCHAR2( 200 );
BEGIN
P_ID_CURSO := 2017;
P_ID_ACTO := 1;
P_ID_VACANTE := 10003;
RetVal :=
EPACF.EPACF_PCK_ACTO_CANDIDATO_VAC.F_OBTENER_ACTO_CANDIDATOS_VAC
(
P_ID_CURSO,
P_ID_ACTO,
P_ID_VACANTE
);
DBMS_OUTPUT.PUT_LINE
(
'El resultado ' || RETVAL%ROWCOUNT
);
LOOP
FETCH RETVAL
INTO V_DTP_NIF,
V_DTP_NOMBRE,
V_DTP_APELLIDO1,
V_DTP_APELLIDO2,
V_DTP_FECHA_NACIMIENTO,
V_DTP_TELEFONO,
V_DTP_EMAIL,
V_ACF_ID_COLECTIVO,
V_ACF_DESCRIPCION,
V_ACF_TIPO_RESOLUCION,
V_ACF_DESC_TIPO_RESOLUCION,
V_DTM_GESTOR,
V_DTM_DESC_GESTOR,
V_ACF_ID_ESTADO,
V_ACF_DESC_ESTADO;
EXIT WHEN RETVAL%NOTFOUND;
DBMS_OUTPUT.PUT_LINE
(
'El resultado '
|| RETVAL%ROWCOUNT
|| ' '
|| V_DTP_NIF
|| ' V_DTP_NIF '
|| V_DTP_NOMBRE
|| ' V_DTP_NOMBRE '
|| V_DTP_APELLIDO1
|| ' V_DTP_APELLIDO1 '
|| V_DTP_APELLIDO2
|| ' V_DTP_APELLIDO2 '
|| V_DTP_FECHA_NACIMIENTO
|| ' V_DTP_FECHA_NACIMIENTO '
|| V_DTP_TELEFONO
|| ' V_DTP_TELEFONO '
|| V_DTP_EMAIL
|| ' V_DTP_EMAIL '
|| V_ACF_ID_COLECTIVO
|| ' V_ACF_ID_COLECTIVO '
|| V_ACF_DESCRIPCION
|| ' V_ACF_DESCRIPCION '
|| V_ACF_TIPO_RESOLUCION
|| ' V_ACF_TIPO_RESOLUCION '
|| V_ACF_DESC_TIPO_RESOLUCION
|| ' V_ACF_DESC_TIPO_RESOLUCION '
|| V_DTM_GESTOR
|| ' V_DTM_GESTOR '
|| V_DTM_DESC_GESTOR
|| ' V_DTM_DESC_GESTOR '
|| V_ACF_ID_ESTADO
|| ' V_ACF_ID_ESTADO '
|| V_ACF_DESC_ESTADO
);
END LOOP;
--COMMIT;
END;
de esta forma se lee el contenido del cursor
- funciona tanto en
SQL
como desde elTOAD
- funciona tanto en
ver una forma rápida de sacar la información que retorna una función con un
refcursor
con muchos campos.¿sacando los campos de la
select
que crea el cursor? EER * [Error] Execution (2: 1): ORA-06504: PL/SQL: Los tipos de retorno de variables de juego de resultados o la consulta no coinciden ORA-06512: en línea 123spool
para crearlo desde PL/SQL Developer Archivo / Nuevo / ventana de comandos
configuración de spool
para configurar una salida y generar por ejemplo un conjunto de
inserts
spool c:\inicio\ficheroSpool.sql
SET HEADING OFF
SET FEEDBACK OFF
SET LINESIZE 1000
SET TERMOUT OFF -- anula la salida por pantalla lo que reduce el tiempo de creación del fichero
PROMPT SET TERMOUT OFF
SELECT 'insert into T_PET (cod_cue, cod_esp, pos_can, nif, nombre, pos_pet,vac, EST_PET,num_fila) values (''' || P.COD_CUE || ''' , ''' || P.COD_ESP || ''' , ' ||POS_CAN || ' , ''' || P.NIF || ''' , ''' || P.NOMBRE || ''' , ' ||POS_PET || ' , ' || VAC || ' , ' || 0 || ' , ' || NUM_FILA || ' );' || DECODE(MOD(ROWNUM, 10), 0, CHR(10) || 'COMMIT;', NULL) FROM T_PET P WHERE P.NUM_FILA < 11 ORDER BY P.NUM_FILA
/
spool off
- lleva incluido el commit cada 11 filas
el fichero generado lleva la opción de anular la salida por pantalla
otras opciones de spool por explorar
SET HEADING OFF
SET FEEDBACK OFF
SET SERVEROUTPUT ON SIZE 1000000
SET PAGESIZE 0
SET FEEDBACK OFF
SET LINESIZE 4096
SET VERIFY OFF
SET TERMOUT OFF
SELECT 1 FROM DUAL;
ejemplo de spool con commit
spool c:\inicio\ficheroSpool.sql
SET HEADING OFF
SET FEEDBACK OFF
SET LINESIZE 1000
SET TERMOUT OFF -- anula la salida por pantalla lo que reduce el tiempo de creación del fichero
PROMPT SET TERMOUT OFF
SELECT 'insert into EPACT_ACTO_ADJ_VAC_SOL
(ACT_ID_CURSO, ACT_ID_ACTO, ACT_ID_VACANTE, ACT_ID_CUERPO, ACT_ID_ESPECIALIDAD,
ACT_NIF, ACT_ORDEN, ACT_ID_ESTADO)
values ( 2017, 1,'|| V.N_VACANTE||','''|| V.C_CUERPO||''','''|| V.C_ESPEC||''' , ''' ||FB_DNI_A_NIF(P.DNI)||''' , '||P.N_ORD_PREF ||', '||
'''PENDIENTE'' ); '||
DECODE(MOD(ROWNUM,10),0,CHR(10)||CHR(13)||'COMMIT;',NULL)
FROM PETICVAC_AE P, V_VACANTES_AE V
WHERE P.F_CONV_AE = '27/07/2017'
AND P.AÑO_INI_CUR = V.AÑO_INI_CUR
AND P.N_VACANTE = V.N_VACANTE
AND V.C_ESPEC = 'EF1'
and rownum < 12 -- para pruebas
ORDER BY P.N_ORD_PREF;
SELECT 'COMMIT;' FROM DUAL;`
spool off
añadir a un spool
- para añadir información a un spoll y no borarlo se usa
spool <nombre_fichero_spool> append
filas afectadas
- para saber el número de filas afectadas por una sentencia se usa
SQL%ROWCOUNT
CREATE TABLE employees_temp AS SELECT * FROM employees;
BEGIN
UPDATE employees_temp SET salary = salary * 1.05 WHERE salary < 5000;
DBMS_OUTPUT.PUT_LINE('Updated ' || SQL%ROWCOUNT || ' salaries.');
END;
- tiene que estar justo despues de la sentencia.
numero de filas de una consulta
SELECT COD_CUE, COD_ESP, POS_CAN, NIF, NOMBRE, POS_PET, VAC, NIF_ADJ,
EST_PET, NUM_FILA--, COUNT(*) OVER()
tunning
interesante post sobre tunning de consultas
Cuando se hace una consulta multi-tabla con //joins//, el orden en que se ponen las tablas en el FROM influye en el plan de ejecución. Aquellas tablas que retornan más filas deben ir en las primeras posiciones, mientras que las tablas con pocas filas deben situarse al final de la lista de tablas.
Colocar la tabla que devuelve menor número de registros en el último lugar del FROM.
Utilizar siempre que sea posible las mismas consultas. La segunda vez que se ejecuta una consulta, se ahorrará mucho tiempo de parsing y optimización, así que se debe intentar utilizar las mismas consultas repetidas veces.
fiddle
- para probar sentencias sql online
- sqlfiddle
- sqlfiddle ejemplo con oracle
- muestra el plan de ejecucción
- se puede generar un link y compartir
- ejemplo propio :smile:
- el usuario
USER_4_C6E41
forma parte del link generado
trigger
autoincremental
- cuidado con el
trigger
para aumentar el contador cuando empezamos con la tabla vacía
SELECT '1 MAX(NUM)' SENTENCIA , MAX(NUM) FROM AUD_APL UNION
SELECT '2 NVL(MAX(NUM),0)' , NVL(MAX(NUM),0) FROM AUD_APL UNION
SELECT '3 NVL(MAX(NUM),0) + 1' ,NVL(MAX(NUM),0) + 1 FROM AUD_APL UNION
SELECT '4 NVL(NUM,0)' SENTENCIA , NVL(NUM,0) FROM AUD_APL UNION
SELECT '5 MAX(NVL(NUM,0))' , MAX(NVL(NUM,0)) FROM AUD_APL UNION
SELECT '6 MAX(NVL(NUM,0)) + 1' , MAX(NVL(NUM,0)) + 1 FROM AUD_APL
- los ref cursores
-- DESDE SQL PLUS
SQL> conn gab/desarrollo@xe
Connected.
SQL> SHOW USER
USER is "GAB"
SQL> VAR RC REFCURSOR;
SQL> exec :rc := gab.gab_pck_alumno.f_buscar_alumnos (null,null,1);
PL/SQL procedure successfully completed.
SQL> print rc
GAB_ALUMNO
--------------------------------------------------
GAB_DESCRIPCION
--------------------------------------------------------------------------------
ALUMNO1
DESCRIPCION DEL ALUMNO 1
ALUMNO2
DESCRIPCION DEL ALUMNO 2
ALUMNO3
DESCRIPCION DEL ALUMNO 3
SQL> print rc
SP2-0625: Error printing variable "rc"
SQL>
- el
ref cursor
no contiene los datos resultado de la consulta sino que contiene un puntero a la consulta y por eso el segundoprint
da error - el primer
print
funciona porqueSQL Plus
mira el cursor de referencia y ve que es un cursor abierto pues la sentencia no lo cierra. - Entra en el bucle, obteniendo cada fila de datos de la base de datos, utilizando el cursor de referencia como referencia (puntero) a la consulta relevante, y mostrando cada fila de datos hasta que no tenía más filas para recuperar.
- Una vez que se han obtenido todas las filas, cierra el cursor.
SQL> ed
Wrote file afiedt.buf
1 declare
2 c_alumnos sys_refcursor;
3 begin
4 c_alumnos := gab.gab_pck_alumno.f_buscar_alumnos (null,null,1); -- This returns an open cursor
5 dbms_output.put_line('Alumnos: '||c_alumnos%ROWCOUNT);
6 close c_alumnos;
7* end;
SQL> /
Alumnos: 0
PL/SQL procedure successfully completed.
- sale 0 a pesar de que existen 3 alumnos por que
c_alumnos%ROWCOONT
muestra el número de fila leídas a través del cursos y no hemos leído ninguna.
Wrote file afiedt.buf
1 declare
2 c_alumnos sys_refcursor;
3 v_id_alumno varchar2(50);
4 v_descripcion varchar2(200);
5 begin
6 c_alumnos := gab.gab_pck_alumno.f_buscar_alumnos (null,null,1); -- This returns an open cursor
7 dbms_output.put_line('Pre Fetch: Alumnos: '||c_alumnos%ROWCOUNT);
8 fetch c_alumnos into v_id_alumno, v_descripcion;
9 dbms_output.put_line('Post Fetch: Rows: '||c_alumnos%ROWCOUNT);
10 close c_alumnos;
11* end;
SQL> /
Pre Fetch: Alumnos: 0
Post Fetch: Rows: 1
PL/SQL procedure successfully completed.
- ahora sí que ha leído una fila del cursor.
SQL> ed
Wrote file afiedt.buf
1 declare
2 c_alumnos sys_refcursor;
3 v_id_alumno varchar2(50);
4 v_descripcion varchar2(200);
5 begin
6 c_alumnos := gab.gab_pck_alumno.f_buscar_alumnos (null,null,1); -- This returns an open cursor
7 dbms_output.put_line('Pre Fetch: Alumnos: '||c_alumnos%ROWCOUNT);
8 loop
9 fetch c_alumnos into v_id_alumno, v_descripcion;
10 exit when c_alumnos%NOTFOUND;
11 dbms_output.put_line('Post Fetch: Rows: '||c_alumnos%ROWCOUNT||' '||v_id_alumno||' '||v_descripcion);
12 end loop;
13 close c_alumnos;
14* end;
SQL> /
Pre Fetch: Alumnos: 0
Post Fetch: Rows: 1 ALUMNO1 DESCRIPCION DEL ALUMNO 1
Post Fetch: Rows: 2 ALUMNO2 DESCRIPCION DEL ALUMNO 2
Post Fetch: Rows: 3 ALUMNO3 DESCRIPCION DEL ALUMNO 3
PL/SQL procedure successfully completed.
de esta forma se lee el contenido del cursor
- funciona tanto en
SQL
como desde elTOAD
|
SENTENCIA | MAX(NUM) | | :——————-: |:——: |
- funciona tanto en
los ref cursores
-- DESDE SQL PLUS
SQL> conn gab/desarrollo@xe
Connected.
SQL> SHOW USER
USER is "GAB"
SQL> VAR RC REFCURSOR;
SQL> exec :rc := gab.gab_pck_alumno.f_buscar_alumnos (null,null,1);
PL/SQL procedure successfully completed.
SQL> print rc
GAB_ALUMNO
--------------------------------------------------
GAB_DESCRIPCION
--------------------------------------------------------------------------------
ALUMNO1
DESCRIPCION DEL ALUMNO 1
ALUMNO2
DESCRIPCION DEL ALUMNO 2
ALUMNO3
DESCRIPCION DEL ALUMNO 3
SQL> print rc
SP2-0625: Error printing variable "rc"
SQL>
- el
ref cursor
no contiene los datos resultado de la consulta sino que contiene un puntero a la consulta y por eso el segundoprint
da error - el primer
print
funciona porqueSQL Plus
mira el cursor de referencia y ve que es un cursor abierto pues la sentencia no lo cierra. - Entra en el bucle, obteniendo cada fila de datos de la base de datos, utilizando el cursor de referencia como referencia (puntero) a la consulta relevante, y mostrando cada fila de datos hasta que no tenía más filas para recuperar.
- Una vez que se han obtenido todas las filas, cierra el cursor.
SQL> ed
Wrote file afiedt.buf
1 declare
2 c_alumnos sys_refcursor;
3 begin
4 c_alumnos := gab.gab_pck_alumno.f_buscar_alumnos (null,null,1); -- This returns an open cursor
5 dbms_output.put_line('Alumnos: '||c_alumnos%ROWCOUNT);
6 close c_alumnos;
7* end;
SQL> /
Alumnos: 0
PL/SQL procedure successfully completed.
- sale 0 a pesar de que existen 3 alumnos por que
c_alumnos%ROWCOONT
muestra el número de fila leídas a través del cursos y no hemos leído ninguna.
Wrote file afiedt.buf
1 declare
2 c_alumnos sys_refcursor;
3 v_id_alumno varchar2(50);
4 v_descripcion varchar2(200);
5 begin
6 c_alumnos := gab.gab_pck_alumno.f_buscar_alumnos (null,null,1); -- This returns an open cursor
7 dbms_output.put_line('Pre Fetch: Alumnos: '||c_alumnos%ROWCOUNT);
8 fetch c_alumnos into v_id_alumno, v_descripcion;
9 dbms_output.put_line('Post Fetch: Rows: '||c_alumnos%ROWCOUNT);
10 close c_alumnos;
11* end;
SQL> /
Pre Fetch: Alumnos: 0
Post Fetch: Rows: 1
PL/SQL procedure successfully completed.
- ahora sí que ha leído una fila del cursor.
SQL> ed
Wrote file afiedt.buf
1 declare
2 c_alumnos sys_refcursor;
3 v_id_alumno varchar2(50);
4 v_descripcion varchar2(200);
5 begin
6 c_alumnos := gab.gab_pck_alumno.f_buscar_alumnos (null,null,1); -- This returns an open cursor
7 dbms_output.put_line('Pre Fetch: Alumnos: '||c_alumnos%ROWCOUNT);
8 loop
9 fetch c_alumnos into v_id_alumno, v_descripcion;
10 exit when c_alumnos%NOTFOUND;
11 dbms_output.put_line('Post Fetch: Rows: '||c_alumnos%ROWCOUNT||' '||v_id_alumno||' '||v_descripcion);
12 end loop;
13 close c_alumnos;
14* end;
SQL> /
Pre Fetch: Alumnos: 0
Post Fetch: Rows: 1 ALUMNO1 DESCRIPCION DEL ALUMNO 1
Post Fetch: Rows: 2 ALUMNO2 DESCRIPCION DEL ALUMNO 2
Post Fetch: Rows: 3 ALUMNO3 DESCRIPCION DEL ALUMNO 3
PL/SQL procedure successfully completed.
- de esta forma se lee el contenido del cursor
- funciona tanto en
SQL
como desde elTOAD
| 1 MAX(NUM) | | | 2 NVL(MAX(NUM),0) | 0 | | 3 NVL(MAX(NUM),0) + 1 | 1 | | 5 MAX(NVL(NUM,0)) | | | 6 MAX(NVL(NUM,0)) + 1 | |
- funciona tanto en
blogs
por explorar
sys.dbms_utility.format_error_backtrace
- interesante entrada sobre el tema
sqlplus
OWA_UTIL.CALENDARPRINT
- leo que genera un calendario en
HTML
- uso del paquete
owa_util.calendarprint
pero me da un error
-- como GAB
SQL> exec OWA_UTIL.CALENDARPRINT(P_QUERY => 'SELECT sysdate ,gab_alumno, null FROM gab_alumno where gab_alumno = ''ALUMNO1'' order by sysdate', P_MF_ONLY =>'Y');
BEGIN OWA_UTIL.CALENDARPRINT(P_QUERY => 'SELECT sysdate ,gab_alumno, null FROM gab_alumno where gab_alumno = ''ALUMNO1'' order by sysdate', P_MF_ONLY =>'Y'); END;
*
ERROR at line 1:
ORA-06502: PL/SQL: numeric or value error: character string buffer too small
ORA-06512: at "SYS.OWA_UTIL", line 2356
ORA-06512: at line 1
SQL> exec OWA_UTIL.CALENDARPRINT(P_QUERY => 'SELECT sysdate ,gab_alumno, null FROM gab_alumno order by gab_alumno', P_MF_ONLY =>'Y');
pl-sql developer
- para ejecutar la sentencia en la que esta el cursor
- Tools-preferences-SQL-window