Las tablas de bases de datos no son entidades estáticas; Además de los eventos habituales de inserción/actualización/eliminación, se pueden ejecutar DDL ocasionales para agregar columnas, eliminar columnas o agregar restricciones o índices necesarios. Los dos primeros elementos pueden crear problemas con los procedimientos almacenados, los paquetes, las funciones y posiblemente los disparadores al cambiar la cantidad de columnas que deben procesarse cuando se usan variables explícitas. Si el programador utilizó una variable de registro (como se muestra a continuación), es probable que no se observen problemas:
SPLEEBO @ gwankus > SPLEEBO @ gwankus > -- SPLEEBO @ gwankus > -- Code to display all employee information SPLEEBO @ gwankus > -- SPLEEBO @ gwankus > -- This should succeed SPLEEBO @ gwankus > -- SPLEEBO @ gwankus > declare 2 cursor get_emp_info is 3 select * From emp; 4 begin 5 for emp_rec in get_emp_info loop 6 dbms_output.put_line(emp_rec.empno||' '||emp_rec.ename||' '||emp_rec.job||' '||emp_rec.mgr||' '||emp_rec.hiredate||' '||emp_rec.sal||' '||emp_rec.comm||' '||emp_rec.deptno); 7 end loop; 8 end; 9 / 7369 SMITH CLERK 7902 17-DEC-80 800 20 7499 ALLEN SALESMAN 7698 20-FEB-81 1600 300 30 7521 WARD SALESMAN 7698 22-FEB-81 1250 500 30 7566 JONES MANAGER 7839 02-APR-81 2975 20 7654 MARTIN SALESMAN 7698 28-SEP-81 1250 1400 30 7698 BLAKE MANAGER 7839 01-MAY-81 2850 30 7782 CLARK MANAGER 7839 09-JUN-81 2450 10 7788 SPLEEBO ANALYST 7566 09-DEC-82 3000 20 7839 KING PRESIDENT 17-NOV-81 5000 10 7844 TURNER SALESMAN 7698 08-SEP-81 1500 0 30 ... 6100 MILLER CLERK 7782 23-SEP-97 1300 10 SPLEEBO @ gwankus >
Si se utilizan variables explícitas, es probable que el código falle debido al cambio. Puede ser desconcertante ver lo siguiente en un mensaje de error:
PLS-00394: wrong number of values in the INTO list of a FETCH statement
Por supuesto, la investigación revelará que se ejecutó una instrucción "alterar tabla..." antes de la falla o una descripción en la tabla afectada informará un número diferente de columnas que las que estaban presentes cuando se escribió originalmente el código. Una vez que se conoce este hecho, solucionar el problema depende del desarrollador que lo escribió. Tendrán opciones existentes sobre cómo realizar dichos cambios.
Mirando un ejemplo usando variables codificadas explícitamente y una columna agregada, exploremos usando un procedimiento del paquete DBMS_UTILITY, EXPAND_SQL_TEXT, para generar una lista completa de columnas de la tabla modificada para usar como referencia para los cambios de código. Primero, el código original y el error que genera:
SPLEEBO @ gwankus > SPLEEBO @ gwankus > -- SPLEEBO @ gwankus > -- Code to display all employee information SPLEEBO @ gwankus > -- SPLEEBO @ gwankus > -- Fails because not enough variables are declared SPLEEBO @ gwankus > -- and populated SPLEEBO @ gwankus > -- SPLEEBO @ gwankus > declare 2 v_empno emp.empno%type; 3 v_ename emp.ename%type; 4 v_job emp.job%type; 5 v_mgr emp.mgr%type; 6 v_hiredate emp.hiredate%type; 7 v_sal emp.sal%type; 8 v_comm emp.comm%type; 9 v_deptno emp.deptno%type; 10 11 cursor get_emp_info is 12 select * From emp; 13 begin 14 open get_emp_info; 15 loop 16 fetch get_emp_info into v_empno, v_ename, v_job, v_mgr, v_hiredate, v_sal, v_comm, v_deptno; 17 exit when get_emp_info%notfound; 18 dbms_output.put_line(v_empno||' '||v_ename||' '||v_job||' '||v_mgr||' '||v_hiredate||' '||v_sal||' '||v_comm||' '||v_deptno); 19 end loop; 20 end; 21 / fetch get_emp_info into v_empno, v_ename, v_job, v_mgr, v_hiredate, v_sal, v_comm, v_deptno; * ERROR at line 16: ORA-06550: line 16, column 3: PLS-00394: wrong number of values in the INTO list of a FETCH statement ORA-06550: line 16, column 3: PL/SQL: SQL Statement ignored SPLEEBO @ gwankus > SPLEEBO @ gwankus >
Cree una copia del código original (para conservarlo en caso de que sea necesario) y use DBMS_UTILITY.EXPAND_SQL_TEXT para generar, como comentario, los resultados ampliados de una consulta "seleccionar * de..." contra la tabla modificada. El procedimiento requiere que se declare una variable CLOB para contener los resultados de la llamada al procedimiento y también requiere la consulta específica 'seleccionar *' para operar. El código que se muestra a continuación genera esta salida como un comentario y se puede reutilizar editando el nombre de la tabla en la consulta proporcionada:
SPLEEBO @ gwankus > -- SPLEEBO @ gwankus >-- Expand the 'select *' query to see all SPLEEBO @ gwankus >-- of the returned columns SPLEEBO @ gwankus >-- SPLEEBO @ gwankus >-- Add the output to the failing script SPLEEBO @ gwankus >-- to facilitate corrective edits SPLEEBO @ gwankus >-- SPLEEBO @ gwankus >spool new_query.sql SPLEEBO @ gwankus >declare 2 l_clob clob; 3 begin 4 dbms_utility.expand_sql_text ( 5 input_sql_text => 'select * from emp', 6 output_sql_text => l_clob 7 ); 8 9 dbms_output.put_line('/*'); 10 dbms_output.put_line(lower(l_clob)); 11 dbms_output.put_line('*/'); 12 end; 13 / /* select "a1"."empno" "empno","a1"."ename" "ename","a1"."job" "job","a1"."mgr" "mgr","a1"."hiredate" "hiredate","a1"."sal" "sal","a1"."comm" "comm","a1"."deptno" "deptno","a1"."term_dt" "term_dt" from "scott"."emp" "a1" */ PL/SQL procedure successfully completed. SPLEEBO @ gwankus > spool off
Cree una copia de trabajo y agréguele el resultado de la consulta anterior. Luego, abra el archivo modificado en el editor de su elección para realizar los cambios necesarios:
SPLEEBO @ gwankus > -- SPLEEBO @ gwankus > -- Copy the original script to preserve code SPLEEBO @ gwankus > -- SPLEEBO @ gwankus > !cp emp_info_pl_orig.sql emp_info_pl.sql SPLEEBO @ gwankus > -- SPLEEBO @ gwankus > -- Add the output generated above as a comment SPLEEBO @ gwankus > -- for reference purposes SPLEEBO @ gwankus > -- SPLEEBO @ gwankus > !cat new_query.sql >> emp_info_pl.sql SPLEEBO @ gwankus > SPLEEBO @ gwankus > -- SPLEEBO @ gwankus > -- Edit the script copy to fix the issue by SPLEEBO @ gwankus > -- adding the necessary variable declaration SPLEEBO @ gwankus > -- and editing the code to populate it SPLEEBO @ gwankus > -- SPLEEBO @ gwankus > !vi emp_info_pl.sql declare v_empno emp.empno%type; v_ename emp.ename%type; v_job emp.job%type; v_mgr emp.mgr%type; v_hiredate emp.hiredate%type; v_sal emp.sal%type; v_comm emp.comm%type; v_deptno emp.deptno%type; v_term_dt emp.term_dt%type; cursor get_emp_info is select * From emp; begin open get_emp_info; loop fetch get_emp_info into v_empno, v_ename, v_job, v_mgr, v_hiredate, v_sal, v_comm, v_deptno, v_term_dt; exit when get_emp_info%notfound; dbms_output.put_line(v_empno||' '||v_ename||' '||v_job||' '||v_mgr||' '||v_hiredate||' '||v_sal||'
'||v_comm||' '||v_deptno||' '||v_term_dt); end loop; end; / /* select "a1"."empno" "empno","a1"."ename" "ename","a1"."job" "job","a1"."mgr" "mgr","a1"."hiredate" "hiredate","a1"."sal" "sal","a1"."comm" "comm","a1"."deptno" "deptno","a1"."term_dt" "term_dt" from "scott"."emp" "a1" */
Pruebe los cambios para asegurarse de que todo funcione como se esperaba:
SPLEEBO @ gwankus > SPLEEBO @ gwankus > set head on feedback on pagesize 60 SPLEEBO @ gwankus > -- SPLEEBO @ gwankus > -- Run modified code SPLEEBO @ gwankus > -- SPLEEBO @ gwankus > -- The anonymous block now completes SPLEEBO @ gwankus > -- without error SPLEEBO @ gwankus > -- SPLEEBO @ gwankus > @emp_info_pl SPLEEBO @ gwankus > declare 2 v_empno emp.empno%type; 3 v_ename emp.ename%type; 4 v_job emp.job%type; 5 v_mgr emp.mgr%type; 6 v_hiredate emp.hiredate%type; 7 v_sal emp.sal%type; 8 v_comm emp.comm%type; 9 v_deptno emp.deptno%type; 10 v_term_dt emp.term_dt%type; 11 12 cursor get_emp_info is 13 select * From emp; 14 begin 15 open get_emp_info; 16 loop 17 fetch get_emp_info into v_empno, v_ename, v_job, v_mgr, v_hiredate, v_sal, v_comm, v_deptno, v_term_dt; 18 exit when get_emp_info%notfound; 19 dbms_output.put_line(v_empno||' '||v_ename||' '||v_job||' '||v_mgr||' '||v_hiredate||' '||v_sal||' '||v_comm||' '||v_deptno||' '||v_term_dt); 20 end loop; 21 end; 22 / 7369 SMITH CLERK 7902 17-DEC-80 800 20 31-DEC-99 7499 ALLEN SALESMAN 7698 20-FEB-81 1600 300 30 31-DEC-99 7521 WARD SALESMAN 7698 22-FEB-81 1250 500 30 31-DEC-99 7566 JONES MANAGER 7839 02-APR-81 2975 20 31-DEC-99 7654 MARTIN SALESMAN 7698 28-SEP-81 1250 1400 30 31-DEC-99 7698 BLAKE MANAGER 7839 01-MAY-81 2850 30 31-DEC-99 7782 CLARK MANAGER 7839 09-JUN-81 2450 10 31-DEC-99 7788 SPLEEBO ANALYST 7566 09-DEC-82 3000 20 31-DEC-99 7839 KING PRESIDENT 17-NOV-81 5000 10 31-DEC-99 ... 6100 MILLER CLERK 7782 23-SEP-97 1300 10 31-DEC-99 PL/SQL procedure successfully completed. SPLEEBO @ gwankus > --
Esta misma técnica se puede utilizar en tablas con un número relativamente grande de columnas:
SPLEEBO @ gwankus > -- SPLEEBO @ gwankus > -- Let's take another example SPLEEBO @ gwankus > -- SPLEEBO @ gwankus > -- Create a table with 21 columns SPLEEBO @ gwankus > -- and populate it SPLEEBO @ gwankus > -- SPLEEBO @ gwankus > @lotsa_cols SPLEEBO @ gwankus > create table lotsacols( 2 a1 number, 3 a2 number, 4 a3 number, 5 a4 number, 6 a5 number, 7 a6 number, 8 a7 number, 9 a8 number, 10 a9 number, 11 a10 number, 12 a11 number, 13 a12 number, 14 a13 number, 15 a14 number, 16 a15 number, 17 a16 number, 18 a17 number, 19 a18 number, 20 a19 number, 21 a20 number, 22 a21 number); Table created. SPLEEBO @ gwankus > SPLEEBO @ gwankus > begin 2 for z in 1..1000 loop 3 insert into lotsacols(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20,a21) 4 values(mod(z,3)+1,mod(z,13)+1,mod(z,21)+1,mod(z,34)+1,mod(z,47)+1,mod(z,53)+1,
mod(z,67)+1,mod(z,79)+1,mod(z,81)+1,mod(z,97)+1,mod(z,3)+1,mod(z,7)+1,mod(z,6)+1,mod(z,2)+1,
mod(z,9)+1,mod(z,8)+1,mod(z,101)+1,mod(z,407)+1,mod(z,313)+1,mod(z,271)+1,mod(z,133)+1); 5 end loop; 6 7 commit; 8 end; 9 / PL/SQL procedure successfully completed. SPLEEBO @ gwankus > SPLEEBO @ gwankus > SPLEEBO @ gwankus > -- SPLEEBO @ gwankus > -- Rather than do a DESC on the table SPLEEBO @ gwankus > -- use expand_sql_text to generate the SPLEEBO @ gwankus > -- column list and spool it to a file for SPLEEBO @ gwankus > -- later use SPLEEBO @ gwankus > -- SPLEEBO @ gwankus > -- Edit that file to create a working block SPLEEBO @ gwankus > -- of PL/SQL to generate results from the SPLEEBO @ gwankus > -- table data SPLEEBO @ gwankus > -- /* select "a1"."a1" "a1","a1"."a2" "a2","a1"."a3" "a3","a1"."a4" "a4","a1"."a5" "a5","a1"."a6" "a6","a1"."a7" "a7","a1"."a8" "a8","a1"."a9" "a9","a1"."a10" "a10","a1"."a11" "a11","a1"."a12" "a12","a1"."a13" "a13","a1"."a14" "a14","a1"."a15" "a15","a1"."a16" "a16","a1"."a17" "a17","a1"."a18" "a18","a1"."a19" "a19","a1"."a20" "a20","a1"."a21" "a21" from "scott"."lotsacols" "a1" */ declare cursor get_lotsa is select * From lotsacols; begin dbms_output.put_line('Your lucky LOTTO numbers are: '); for lotsa in get_lotsa loop dbms_output.put_line(lotsa.a1||' '||lotsa.a6||' '||lotsa.a7||' '||lotsa.a13||' '||lotsa.a17||' '||lotsa.a20); end loop; end; / SPLEEBO @ gwankus > -- SPLEEBO @ gwankus > -- Execute the code SPLEEBO @ gwankus > -- Your lucky LOTTO numbers are: 2 50 8 5 7 209 3 51 9 6 8 210 1 52 10 1 9 211 ... 2 53 11 2 10 212 1 32 51 1 14 179 2 33 52 2 15 180 3 34 53 3 16 181 PL/SQL procedure successfully completed.
Usar EXPAND_SQL_TEXT puede ser más fácil que generar una lista de tablas usando DESC y poner en cola los resultados, ya que crea un archivo más pequeño que se puede incorporar fácilmente en un procedimiento de cambio. Dado que el texto SQL expandido se genera como un comentario, puede permanecer después de que se completen las ediciones en caso de que se requieran o deseen más cambios en el código.
La elección es del desarrollador, pero ciertamente parece más fácil dejar que Oracle genere resultados utilizables de una manera un tanto automatizada para facilitar las ediciones de código. Al final, lo que más importa es aquello con lo que el desarrollador se sienta cómodo. Pero, podría valer la pena investigar el uso de EXPAND_SQL_TEXT para poner esa información de referencia en el script que se está editando y posiblemente evitar perderse entre dos pantallas de código. Eso podría ahorrar tiempo de edición.
###
Ver artículos de David Fitzjarrell