Cursor Problematico
Tengo poca idea de PL/SQL pero desde luego no sabia que me atrancaria en esto. Intento hacer una funcion a la que se le pase el nombre de una tabla y una columna (ha de ser una columna NUMBER) y que encuentre en ella el primer numero que falte. si en la columna hay 1,2,3,5,6 la funcion devolveria 4, vean por favor:
FUNCTION busca_libre (tabla IN VARCHAR2, columna IN VARCHAR2) RETURN NUMBER IS
libre NUMBER;
CURSOR numeros IS SELECT c FROM t ORDER BY c;
actual numeros%ROWTYPE;
BEGIN
libre:=1;
FOR actual IN numeros LOOP
IF actual.c <> libre THEN RETURN (actual);
ELSE
libre := libre+1;
END IF;
END LOOP;
RETURN (libre);
END;
El error que me da es el 201: "el identificador 't' debe declararse. Tambien que la declaracion de numeros esta incompleta.
No se, creo que tiene que ser una tonteria pero no doy con ello.
Gracias de antemano.
FUNCTION busca_libre (tabla IN VARCHAR2, columna IN VARCHAR2) RETURN NUMBER IS
libre NUMBER;
CURSOR numeros IS SELECT c FROM t ORDER BY c;
actual numeros%ROWTYPE;
BEGIN
libre:=1;
FOR actual IN numeros LOOP
IF actual.c <> libre THEN RETURN (actual);
ELSE
libre := libre+1;
END IF;
END LOOP;
RETURN (libre);
END;
El error que me da es el 201: "el identificador 't' debe declararse. Tambien que la declaracion de numeros esta incompleta.
No se, creo que tiene que ser una tonteria pero no doy con ello.
Gracias de antemano.
El problema es con "actual" que lo definiste tanto como una varible --> actual numeros%rowtype y tambien lo pusiste como alias del cursor
for actual in numero loop.
Eliminá actual numeros%rowtype
y dejá IF actual.c <> libre THEN RETURN (actual.c)
QuedarÃa ( creo )
FUNCTION busca_libre (tabla IN VARCHAR2, columna IN VARCHAR2) RETURN NUMBER IS
libre NUMBER;
CURSOR numeros IS SELECT c FROM t ORDER BY c;
BEGIN
libre:=1;
FOR actual IN numeros LOOP
IF actual.c <> libre THEN RETURN (actual.c);
ELSE
libre := libre+1;
END IF;
END LOOP;
RETURN (libre);
END;
for actual in numero loop.
Eliminá actual numeros%rowtype
y dejá IF actual.c <> libre THEN RETURN (actual.c)
QuedarÃa ( creo )
FUNCTION busca_libre (tabla IN VARCHAR2, columna IN VARCHAR2) RETURN NUMBER IS
libre NUMBER;
CURSOR numeros IS SELECT c FROM t ORDER BY c;
BEGIN
libre:=1;
FOR actual IN numeros LOOP
IF actual.c <> libre THEN RETURN (actual.c);
ELSE
libre := libre+1;
END IF;
END LOOP;
RETURN (libre);
END;
QUE RAPIDEZ, pero siento decir que no, tenia ese error tambien pero no es.
He cambiado algo que estaba mal en el anterior codigo, ahora esta asi:
-----------------------------------
FUNCTION busca_libre (tabla IN VARCHAR2, columna IN VARCHAR2) RETURN NUMBER IS
libre NUMBER;
CURSOR numeros IS SELECT columna FROM tabla ORDER BY numero;
BEGIN
libre:=1;
FOR actual IN numeros LOOP
IF actual.columna <> libre THEN RETURN (actual.columna);
ELSE
libre := libre+1;
END IF;
END LOOP;
RETURN (libre);
END;
---------------------------------------------
El problema es que no se traga que el nombre de la tabla donde hacer el select le venga dado por una variable de entrada, cree que es el nombre de la tabla en si y como no existe la tabla "tabla" se queja.
Qu me sugeris, he leido algo de SQL dinamico ¿eso como va?
He cambiado algo que estaba mal en el anterior codigo, ahora esta asi:
-----------------------------------
FUNCTION busca_libre (tabla IN VARCHAR2, columna IN VARCHAR2) RETURN NUMBER IS
libre NUMBER;
CURSOR numeros IS SELECT columna FROM tabla ORDER BY numero;
BEGIN
libre:=1;
FOR actual IN numeros LOOP
IF actual.columna <> libre THEN RETURN (actual.columna);
ELSE
libre := libre+1;
END IF;
END LOOP;
RETURN (libre);
END;
---------------------------------------------
El problema es que no se traga que el nombre de la tabla donde hacer el select le venga dado por una variable de entrada, cree que es el nombre de la tabla en si y como no existe la tabla "tabla" se queja.
Qu me sugeris, he leido algo de SQL dinamico ¿eso como va?
¡JA! lo más importante te lo habÃas guardado. No podés pasar la tabla como variable a menos que uses sql dinámico O, mejora para este caso, que abras el cursor con "open ..."
DEclare
type tipo_cursor is ref cursor;
referencia tipo_cursor;
mi_registro mi_tabla%rowtype;
begin
open referencia for 'select campo1, campoN from ' || mi_variable_nombre_tabla || ' where ...'
loop
fetch referencia into mi_registro;
exit when referencia%notfound;
... tu código llamando a los campos mi_registro.campo1 etc
end;
DEclare
type tipo_cursor is ref cursor;
referencia tipo_cursor;
mi_registro mi_tabla%rowtype;
begin
open referencia for 'select campo1, campoN from ' || mi_variable_nombre_tabla || ' where ...'
loop
fetch referencia into mi_registro;
exit when referencia%notfound;
... tu código llamando a los campos mi_registro.campo1 etc
end;
Lo siento he intentado eso sin exito. Detras de CURSOR nombre_cursor IS parece que solo acepta una sentencia SELECT bien formada.
Se que eso se lo tiene uno que poder saltar pero no se como. He leido sobre SQL dinamico, me da igual los problemas de seguridad y/o eficiencia que puediese tener, ¿puedes decirme la declaracion de un cursor con la columna y el nombre de la tabla pasados por variable?
Jo es que me parece extraño que una cosa tan sencilla y creo que util sea tan complicada de hacer. Menudo lenguaje.
Gracias
Se que eso se lo tiene uno que poder saltar pero no se como. He leido sobre SQL dinamico, me da igual los problemas de seguridad y/o eficiencia que puediese tener, ¿puedes decirme la declaracion de un cursor con la columna y el nombre de la tabla pasados por variable?
Jo es que me parece extraño que una cosa tan sencilla y creo que util sea tan complicada de hacer. Menudo lenguaje.
Gracias
PERO ya NO tenés que hacer "cursor nn is select ..."
Ahora lo re-emplazás por oper referencia for ...
que sà acepta variables porque es un string.
RevÃsá el ejemplo que te escribÃ.
Ahora lo re-emplazás por oper referencia for ...
que sà acepta variables porque es un string.
RevÃsá el ejemplo que te escribÃ.
Ya, ya, eso hice pero me da error que dice:
Se ha encontrado el simbolo 'select numero from ' cuando se esperaba SELECT...
Donde 'select numero from ' es lo que usted dice que ponga 'select campo1, campoN from '.
Por eso le digo que se empeña en que meta un SELECT bien formado, no me explique antes.
Por si sirve de algo uso Oracle9i de BD y Forms6i. He leido cosas extrañisimas sobre soporte para cursores.
Muchas gracias de todas maneras.
Se ha encontrado el simbolo 'select numero from ' cuando se esperaba SELECT...
Donde 'select numero from ' es lo que usted dice que ponga 'select campo1, campoN from '.
Por eso le digo que se empeña en que meta un SELECT bien formado, no me explique antes.
Por si sirve de algo uso Oracle9i de BD y Forms6i. He leido cosas extrañisimas sobre soporte para cursores.
Muchas gracias de todas maneras.
DeberÃas montarte un string que contuviese la sentencia select completa y luego que ejecute el contenido de dicha variable.
Te tendrÃas que declarar aparte de las variables que ya tienes, otras dos:
-- Sentencia SQL. Se ejecuta dinamicamente
vc_Sql VARCHAR2(2000);
-- Cursor dinamico para obtener el retorno
cRET INTEGER;
En la variable vc_Sql metes toda la sentencia SQL y para ejecutarla:
-- Lanzar la sentencia SQL para obtener el número
-- Abrimos un cursor.
cRET := DBMS_SQL.OPEN_CURSOR;
-- Guardamos la sentencia y definimos la estructura
DBMS_SQL.PARSE(cRET, vc_Sql, dbms_sql.native);
DBMS_SQL.DEFINE_COLUMN(cRET,1,Numero);
-- Ejecutamos y realizamos el Fetch
ret := DBMS_SQL.EXECUTE(cRET);
ret := DBMS_SQL.FETCH_ROWS(cRET);
-- Obtenemos el número a devolver
IF ret >0 THEN
-- Va todo bien
DBMS_SQL.COLUMN_VALUE(cRET,1,Numero );
ELSE
-- Indica que no existe ningun registro
Numero := 99999;
END IF;
-- Cerramos el cursor
DBMS_SQL.CLOSE_CURSOR(cRET);
Te tendrÃas que declarar aparte de las variables que ya tienes, otras dos:
-- Sentencia SQL. Se ejecuta dinamicamente
vc_Sql VARCHAR2(2000);
-- Cursor dinamico para obtener el retorno
cRET INTEGER;
En la variable vc_Sql metes toda la sentencia SQL y para ejecutarla:
-- Lanzar la sentencia SQL para obtener el número
-- Abrimos un cursor.
cRET := DBMS_SQL.OPEN_CURSOR;
-- Guardamos la sentencia y definimos la estructura
DBMS_SQL.PARSE(cRET, vc_Sql, dbms_sql.native);
DBMS_SQL.DEFINE_COLUMN(cRET,1,Numero);
-- Ejecutamos y realizamos el Fetch
ret := DBMS_SQL.EXECUTE(cRET);
ret := DBMS_SQL.FETCH_ROWS(cRET);
-- Obtenemos el número a devolver
IF ret >0 THEN
-- Va todo bien
DBMS_SQL.COLUMN_VALUE(cRET,1,Numero );
ELSE
-- Indica que no existe ningun registro
Numero := 99999;
END IF;
-- Cerramos el cursor
DBMS_SQL.CLOSE_CURSOR(cRET);
Asà te complicás la vida.
No se lo que estarás haciendo pero lo que te dije NO puede fallar.
Copiá y pegá esto en tu entorno y vas a ver que funciona ( TESTEADO ).
create table tito_x ( valor varchar2(100) );
insert into tito_x values ( \'HOLA\' );
insert into tito_x values ( \'MUNDO\' );
commit;
declare
type mi_reg is ref cursor;
referencia mi_reg;
registro tito_x%rowtype;
var1 varchar2(100) := \'select\';
var2 varchar2(100) := \'valor\';
var3 varchar2(100) := \'from\';
var4 varchar2(100) := \'tito_x\';
begin
open referencia for var1 || \' \' || var2 || \' \' || var3 || \' \' || var4;
loop
fetch referencia into registro;
exit when referencia%notfound;
dbms_output.put_line( registro.valor );
end loop;
end;
/
-- La salida de esto es
HOLA
MUNDO
No se lo que estarás haciendo pero lo que te dije NO puede fallar.
Copiá y pegá esto en tu entorno y vas a ver que funciona ( TESTEADO ).
create table tito_x ( valor varchar2(100) );
insert into tito_x values ( \'HOLA\' );
insert into tito_x values ( \'MUNDO\' );
commit;
declare
type mi_reg is ref cursor;
referencia mi_reg;
registro tito_x%rowtype;
var1 varchar2(100) := \'select\';
var2 varchar2(100) := \'valor\';
var3 varchar2(100) := \'from\';
var4 varchar2(100) := \'tito_x\';
begin
open referencia for var1 || \' \' || var2 || \' \' || var3 || \' \' || var4;
loop
fetch referencia into registro;
exit when referencia%notfound;
dbms_output.put_line( registro.valor );
end loop;
end;
/
-- La salida de esto es
HOLA
MUNDO
