Secuencias

Por ejemplo tengo un sistema de cargos que tiene una secuencia por cada departamento, ¿existe la posibilidad de tener una multisecuencia sin necesidad de tener que crear una por una?

1 respuesta

Respuesta
1
Lo siento, pero a tu respuesta he de decirte que NO!.
NO exite esa modalidad en el objeto ORACLE : sequence.
Tienes dos alternativas posibles :
1.- Crear como dices una secuencia por departamento, cosa que encuentro muy laboriosa. Y si, aún, tienes la posibilidad de que en un Departamento solo puede actuar un grupo de usuarios y un usuario no puede tratar con varios departamentos, estarías de suerte! Ya que entonces crearías 'n' secuencias, una por cada departamento y darle a todas los permisos de esos grupos de usuarios. Y concederles el mismo sinónimo privado.
Pero aún así sería bastante engorroso de mantener. Y más si los departamentos van creándose y destruyéndose. Según la evolución de la empresa.
2.- Creo que es la mejor posibilidad ante este hecho :
Create una tabla de contadores por departamento. Y en tiempo de inserción (por trigger de BB. DD.) Accede al registro del departamento 'FOR UPDATE' (Bloqueando) inserta el valor y actualiza de inmediato el valor a valor +1. Y haz commit! Para tener el menor tiempo posible el registro bloqueado.
Cuando definas la tabla de 'contadores por departamento' definela como 'CACHE'. De esta forma la tendrás siempre en memoria y el acceso será más rápido.
Piensa también, independientemente de estas soluciones, que a partir de Oracle 10g, las secuencias no pueden ser ORDERED (ordenadas) y CACHE. Ya que desmontaría todo el progreso que ha dado Oracle a su plataforma RAC! Crearía muchísimos bloqueos por petición a la secuencia.
Siente confirmarte que no hay solución posible más que las que te comento.
Suerte.
NOTA : La idea de tener una secuencia que de valores secuenciales para crear una 'primary key', así como Access tenía 'aquellas columnas de aouto-increment', hay que eliminarla en las nuevas versiones de ORACLE (a partir de la 10g). Si no se quiere tener contenciones internas.
Hola Experto... precisamente ya había creado una tabla como contingencia a que no existiera una opción más simple...
La estructura de la Tabla es
/* Tabla Contador de Secuencia */
create table contsecuencia
(
COD_INDITABLA CHAR(2 BYTE) NOT NULL,
COD_CODIGO VARCHAR2(6 BYTE) NOT NULL,
ANN_SECUENCIA CHAR(4 BYTE) NOT NULL,
NUM_SECUENCIA NUMBER(10) DEFAULT 0,
COD_REGIUSUARIO VARCHAR2(20 BYTE) DEFAULT sys_context('userenv','terminal'),
FEC_REGIUSUARIO DATE DEFAULT sysdate,
COD_MODIUSUARIO VARCHAR2(20 BYTE) DEFAULT sys_context('userenv','terminal'),
FEC_MODIUSUARIO DATE DEFAULT sysdate
)
cod_inditabla = Tipo de Secuencia
cod_codigo = Codigo de Departamento / Serie de Comprobante
ann_secuencia = Año de Secuencia
Y tambien cree un procedimiento almacenado con la siguiente instruccion:
CREATE OR REPLACE PROCEDURE Spd_Controlsec (
pa_inditabl VARCHAR2, pa_codisecu VARCHAR2, pa_annosecu VARCHAR2, pa_numsecue IN OUT VARCHAR2
)
IS
BEGIN
/* Cursor cur_registro Is */
Select (num_secuencia+1) num_secuencia Into pa_numsecue From contsecuencia Where cod_inditabla = pa_inditabl And
cod_codigo = pa_codisecu And ann_secuencia = pa_annosecu;
DBMS_OUTPUT.PUT_LINE(pa_numsecue);
If pa_numsecue Is Null Then
DBMS_OUTPUT.PUT_LINE('If');
Insert Into contsecuencia (cod_inditabla, cod_codigo, ann_secuencia, num_secuencia) Values (pa_inditabl, pa_codisecu, pa_annosecu, 1);
pa_numsecue:=1;
Else
DBMS_OUTPUT.PUT_LINE('Else');
Update contsecuencia Set num_secuencia=pa_numsecue Where cod_inditabla = pa_inditabl And
cod_codigo = pa_codisecu And ann_secuencia = pa_annosecu;
End If;
Commit;
END spd_controlsec;
Pero la verdad me llamo la atención de como utilizar un Tigger para hacer esta misma tarea, si pudieras proporcionarme algún ejemplo te lo voy agradecer...
Saludos
Es prácticamente lo mismo que estas escribiendo en el procedimiento almacenado pero en un trigger de BB. DD. Donde vas a utilizar la 'secuencia fictica o multi-secuencia'.
Te creas un trigger sobre la tabla donde insertarás el valor -tan solo para la acción de INSERT- y de esta forma en el resto de los programas que inserten en la tabla queda de forma transparente.
Es decir, si hacer un INSERT en la tabla A, solo colocarás los valores de todas las columnas excepto de la que recibirá el valor de esta 'simulación de secuencia' en tiempo de AFTER INSERT.
Por cierto, en las sentencias SELECT donde vas a buscar el valor del siguiente NÚMERO, tienes que poner al final de la SELECT ... FOR UPDATE; ya que sino, puede que se dé la situación de que otro proceso, coja este mismo registro y tengas valores repetidos.
Si pones 'FOR UPDATE', el siguiente proceso, esperará a recuperar el valor hasta que hayas hecho un 'commit' o 'rollback' (por sipuesto no pongas FOR UPDATE NOWAIT!).
¿Entiendes?
A ver si recuerdo la sintaxis completa ...
Create or replace trigger
BEFORE INSERT
ON TABLE
FOR EACH ROW
Declare
tu código ....
BEGIN
tu código o llamada al package que solo retorne un número
y si lo pegas a ....   :new.;
EXCEPTION
   WHEN ....
END;
/
Recuerda que dentro del 'procedimiento, que yo convertiría en función que retornase un NUMBER, no pongas el 'COMMIT'.
Ya que en tu código de programa, harías por ejemplo ...
INSERT INTO
()
Values
... valores del resto de columnas;
--
COMMIT;
============================================================
Espero que lo entiendas ... y donde pongo significa que tienes que poner situ tabla se llama A, ¿pues A no . Ok?
============================================================
Es más, para ser aún más perfeccionistas y hacer que el proceso tenga el rendimiento más optimo.
Como tu tabla de contadores no tendrá muchos valores (100 o 200) al crearla colócale la clausula 'CACHE' y así la tendrás siempre en memoria y el acceso será mucho más optimo.
Y en tu 'procedimiento' que yo transformaría a 'función' o bien lo dejaría como código (el mismo que tiene el procedimiento -PERO SIN COMMIT!- Lo cargaría en memoria con un KEEP : execute dbms_shared_pool. Keep('').
Piensa que esto se debería de hacer una única vez (el KEEP de la función o procedimiento). Podrías hacerlo nada más arrancar la instancia o en un punto único de entrada a tu aplicación.
Si lo ves demasiado complicado, da igual, no la dejes en memoria. Pero como mínimo intenta que la tabla (si no es muy grande) permanezca en CACHE (memoria).
ALTER TABLE CACHE;
Sólo lo tienes que ejecutar una vez y queda almacenado en el diccionario. No es necesario hacerlo cada vez.
Recuerda que en un trigger de BEFORE haces referencia a las columnas de la tabla con el operador --> :new. Columna
Espero que me hayas entendido y que la solución a parte de solventar la problemática te dé un buen rendimiento. Pero sigue todas las indicaciones.
Ah! Y por supuesto ... antes de hacer y dejarlo como definitivo realiza pruebas.
NOTA : Es más si ya tenias programado el insert en esta tabla, y a la columna de la secuencia le dabas un valor, el trigger cambiará de esta forma el valor que envíes y se sustituirá por el valor calculado por tu procedimiento, función o código implícito en el trigger al hacer :
:new.columna := funcion por ejemplo.
Un Saludo
Ramón
<....
Excelente, modifique el procedimiento y lo convertí en una función...
/*==============================================================*/
/* Function: FNC_CONTROLSEC */
/*==============================================================*/
CREATE OR REPLACE FUNCTION fnc_controlsec (
pa_inditabl VARCHAR2, pa_codisecu VARCHAR2, pa_annosecu VARCHAR2
)
RETURN VARCHAR2
IS
PRAGMA AUTONOMOUS_TRANSACTION;
pa_numsecue Varchar2(10) := 1;
BEGIN
Select num_secuencia Into pa_numsecue From contsecuencia
Where cod_inditabla = pa_inditabl And cod_codigo = pa_codisecu And ann_secuencia = pa_annosecu For Update Of num_secuencia;
Update contsecuencia Set num_secuencia=num_secuencia+1
Where cod_inditabla = pa_inditabl And cod_codigo = pa_codisecu And ann_secuencia = pa_annosecu;
Commit;
RETURN(pa_numsecue+1);
EXCEPTION
WHEN NO_DATA_FOUND
THEN
Insert Into contsecuencia (cod_inditabla, cod_codigo, ann_secuencia, num_secuencia) Values (pa_inditabl, pa_codisecu, pa_annosecu, pa_numsecue);
Commit;
RETURN(pa_numsecue);
when others
THEN
/* Inserta Error en tabla de Log de Errores */
END fnc_controlsec;
/
Y referente al Trigger esta suficientemente claro...
Muchas gracias por la ayuda, seguiremos en contacto.

Añade tu respuesta

Haz clic para o

Más respuestas relacionadas