Cursor Problematico

axel
30 de Agosto del 2004
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.

N?or
30 de Agosto del 2004
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;

axel
30 de Agosto del 2004
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?

N?or
30 de Agosto del 2004
¡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;

axel
30 de Agosto del 2004
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

N?or
30 de Agosto del 2004
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í.

axel
30 de Agosto del 2004
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.

Inma
30 de Agosto del 2004
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);


N?or
30 de Agosto del 2004
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

Hugo
30 de Agosto del 2004
sdfsdf