Ir al contenido


Foto

Como lidiar con los generadores desde el código

generador generator

  • Por favor identifícate para responder
15 respuestas en este tema

#1 cram

cram

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 828 mensajes
  • LocationMisiones, Argentina

Escrito 23 junio 2015 - 08:29

No se si es el lugar correcto, pero quería compartir y poner en "tela de juicio" mi manera de lidiar con los generadores de interbase/firebird.

El tema es que un generador se modifica cuando es llamado (siempre y cuando se le diga), pero normalmente se lo incrementa.

El caso típico del uso de los valores autoincrementados es el ID de una instancia de entidad, por ejemplo ID_CLIENTE (identificador unívoco de una fila de la tabla CLIENTE).

Se haga commit o rollback en una transacción el generador "actúa" igual, con lo que puede ocurrir el caso siguiente: Se incrementa el número de serie de una factura y luego se intenta escribir la información en las tablas correspondientes, luego sucede un error, pero el generador ya "actuó". Entonces será necesario volver al número anterior para que no quede un hueco en la secuencia de números de serie (cosa que no debe ser).

 

Hace poco me encontré con este problema una vez más y decidí darle una resolución diferente:

 

Creé un procedimiento (almacenado) en la base de datos como wrapper de un generador para el número de serie de las ventas, pero con la perticularidad que no incrementa siempre al generador, sino que recibe un parámetro "Inc" que indica el incremento que recibirá éste.


sql
  1. CREATE PROCEDURE nro_serie_venta (inc SMALLINT)
  2. RETURNS (nuevo_nro INTEGER)
  3. AS
  4. BEGIN
  5. nuevo_nro = gen_id(nro_serial_ven_gen, Inc);
  6. suspend;
  7. END

Nro_Serial_Ven_Gen es el generador.

 

En el programa llamo al wrapper con el parámetro Inc puesto a cero (0), y con eso obtengo el valor actual en el parámetro nuevo_nro sin incrementar al generador y lo guardo en una variable del programa (una variable privada del procedimiento) uso esta variable y luego del commit de la transacción de la venta, si todo sale bien, llamo de nuevo al wrapper, con el parámetro Inc puesto a uno (1).


delphi
  1. try
  2. // Commit de la transacción venta de la base de datos desde el módulo de datos venta
  3. dmVen.trVenta.Commit;
  4. finally
  5. // Pasa el parémtro de entrada para incremento en 1
  6. dmVen.spNumSerieVen.ParamByName('Inc').AsSmallint:= 1;
  7. // Ejecuta el procedimiento
  8. dmVen.spNumSerieVen.ExecProc;
  9. end;

Es decir que no tengo que recurrir a ningún proceso extra para volver el número para evitar el hueco, pues si el commit sale mal, no se habrá incrementado el generador.

 

Saludos

 


  • 0

#2 Fenareth

Fenareth

    Advanced Member

  • Moderador
  • PipPipPip
  • 3.486 mensajes
  • LocationMexico City

Escrito 24 junio 2015 - 07:42

Yo también hago uso de generadores en Firebird para controlar los ID's que utilizo en mis aplicaciones pero éstos son, por regla casi general, no visibles al usuario, por lo que el tema de que haya "saltos" en la secuencia de los ID's no es algo que me quite mucho el sueño.

 

Una de las razones importantes para lo que yo considero el uso del generados es, además de la velocidad para recibir el ID que se va a asignar, es el tema de la concurrencia de usuarios a la aplicación. ¿Cómo resuelves con tu solución de incrementar el generador hasta que hayas realizado el guardado de la información para que no te genere algún tipo de problema de concurrencia de usuarios al momento de que dos o más desean generar el mismo documento?

 

Saludox ! :)


  • 1

#3 Rolphy Reyes

Rolphy Reyes

    Advanced Member

  • Moderadores
  • PipPipPip
  • 2.092 mensajes
  • LocationRepública Dominicana

Escrito 24 junio 2015 - 08:18

Saludos.

 

El enfoque y uso que Fenareth explica es el adecuado para los generadores, a mi modo de ver, estos deben ser utilizados simplemente para generar números internos en la base de datos.

 

Si el objetivo es tener una numeración serial de los documentos, creo que una buena practica es auxiliarse de una tabla diferente que guarde dichas secuencias.


  • 1

#4 Delphius

Delphius

    Advanced Member

  • Moderador
  • PipPipPip
  • 6.295 mensajes
  • LocationArgentina

Escrito 24 junio 2015 - 08:50

El asunto pasa por usar los generadores para lo que fueron hechos y no para otro cosa. Un buen ejemplo de lo 2do son la numeración de facturas. Como todos saben, no puede ni debe haber saltos en la facturación; es ILEGAL. Por tanto el uso de generadores para llevar esta numeración no es buena idea.

 

Los generadores como lo han dicho es más de uso interno a modo de contador. Sirve para llevar los IDs, y  que no necesariamente tiene un significado real. Tener saltos en estos no significa nada.

 

El error nuestro viene cuando pretendemos darle un significado real al ID, y sacarlo del contexto de una clave artificial hacia un plano más "físico". Si el ID que pretendemos necesita tener asociado algún valor para el usuario/cliente y en verdad amerita un sentido de linealidad (que no admite duplicados ni saltos) eso da la pauta que hemos detectado un nuevo campo que podría ser una clave candidata. Más ahora en este caso, es claro que el uso de generadores no calza al molde.

 

Saludos,


  • 1

#5 cadetill

cadetill

    Advanced Member

  • Moderadores
  • PipPipPip
  • 994 mensajes
  • LocationEspaña

Escrito 24 junio 2015 - 10:37

Buenas

 

Estoy de acuerdo con los compañeros, para la numeración de una factura (por poner un ejemplo) no deben usarse generadores, precisamente por lo que explicas, que son independientes de la transacción.

 

Para facturas (o albaranes o pedidos o......) lo que suele hacerse es asignar éste numero en el momento de la grabación del registro, ayudándose de una tabla auxiliar (que suele ser lo más rápido) o bien haciendo un select max(campo) sobre la tabla en cuestión.

 

A mi modo de ver, la tabla auxiliar es la mejor solución: bloqueas el registro en tu transacción mediante un update, lees el contenido tranquilamente, grabas tu documento, y haces el commit de todo (liberando el registro de la tabla para otra transacción)

 

Salut!


  • 1

#6 egostar

egostar

    missing my father, I love my mother.

  • Administrador
  • 14.259 mensajes
  • LocationMéxico

Escrito 24 junio 2015 - 11:40

....... Como todos saben, no puede ni debe haber saltos en la facturación; es ILEGAL. Por tanto el uso de generadores para llevar esta numeración no es buena idea. .....

 

Solo agregar que tampoco debería de haber duplicados de facturas, es decir, si una factura no salio bien, lo ideal es cancelar el folio (no borrarlo) y generar una nueva factura. Y ahora con la facturación electrónica el control ya lo tiene la entidad fiscal.

 

Saludos


  • 0

#7 cram

cram

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 828 mensajes
  • LocationMisiones, Argentina

Escrito 24 junio 2015 - 12:35

Gracias por semejantes ideas y críticas.

Debería pensar en el uso de tablas, ya que el uso de la función Max está lejos de mis "ideales" en programación.

 

Uso los IDs como siempre y obviamente son ocultados al usuario o al menos él no los puede controlar. Los uso para relacionar tablas.

El sistema que estoy desarrollando es monousuario y no funciona en un ambiente compartido, por lo cual no hay problemas de concurrencia y me atreví a modificar mis propias reglas y crear una nueva solución.

 

Aun así en ambiente multiusuario, los IDs serían diferentes otra vez, ya que al igual que en el caso del uso de tablas no se podría usar uno solo, sino uno por cada punto de venta. Claro que esto generaría un mantenimiento de locos en la base de datos central o bien una simple rutina que cree los generadores y los wrappers.

 

De todos modos interesante el consenso sobre la cuestión.

 

Saludos

(b)


  • 0

#8 cram

cram

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 828 mensajes
  • LocationMisiones, Argentina

Escrito 24 junio 2015 - 12:38

Acá un artículo interesante que habla sobre el tema:

 

https://firebird21.w...usivo-a-tablas/

(y)


  • 0

#9 Fenareth

Fenareth

    Advanced Member

  • Moderador
  • PipPipPip
  • 3.486 mensajes
  • LocationMexico City

Escrito 24 junio 2015 - 12:43

Gracias por semejantes ideas y críticas.

Debería pensar en el uso de tablas, ya que el uso de la función Max está lejos de mis "ideales" en programación.....

 

De acuerdo... el "MAX" yo lo evito en la medida de lo posible, cuando la tabla crece considerablemente, obtener este valor puede ser un verdadero dolor de cabeza...  :s

 

Saludox ! :)


  • 0

#10 Delphius

Delphius

    Advanced Member

  • Moderador
  • PipPipPip
  • 6.295 mensajes
  • LocationArgentina

Escrito 24 junio 2015 - 10:37

Cadetill, ¿por bloquear el registro a qué te refieres?
¿Bloqueo explícito? Hay varias maneras de hacer un bloqueo...

El uso de MAX, si se puede evitar es mejor obviamente. Ya que su uso, aún con índices, implica recorrer el conjunto de datos para encontrarlo.
Pero bien sabemos que tampoco es pecado usarlo y por algo el buen Firebird como todo motor de base de datos que se precie saber hacer muy bien las cosas.

Saludos,
  • 0

#11 cadetill

cadetill

    Advanced Member

  • Moderadores
  • PipPipPip
  • 994 mensajes
  • LocationEspaña

Escrito 25 junio 2015 - 02:09

Cadetill, ¿por bloquear el registro a qué te refieres?
¿Bloqueo explícito? Hay varias maneras de hacer un bloqueo...

 

Me refiero a bloquear el registro "jugando" con las transacciones y su nivel de aislamiento. Una buena lectura al respecto puede ser este artículo.

 

Salut!


  • 0

#12 Fenareth

Fenareth

    Advanced Member

  • Moderador
  • PipPipPip
  • 3.486 mensajes
  • LocationMexico City

Escrito 25 junio 2015 - 08:04

....
Pero bien sabemos que tampoco es pecado usarlo y por algo el buen Firebird como todo motor de base de datos que se precie saber hacer muy bien las cosas.
....

 

Mi experiencia personal con Firebird me dice: no lo uses a menos que sea absolutamente necesario

 

Saludox ! :)


  • 0

#13 Delphius

Delphius

    Advanced Member

  • Moderador
  • PipPipPip
  • 6.295 mensajes
  • LocationArgentina

Escrito 26 junio 2015 - 01:08

Mi experiencia personal con Firebird me dice: no lo uses a menos que sea absolutamente necesario

 

Saludox ! :)

 

Por supuesto, si se puede evitar mejor. Ahora bien, para que se se le saque buen provecho a los casos en que debemos usar MAX puede crearse un índice sobre el campo en cuestión.

 

Saludos,


  • 0

#14 Nikolas

Nikolas

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 604 mensajes
  • LocationMar del Plata / Bs As / Argentina

Escrito 28 junio 2015 - 10:15

En mi caso los utilizo para referenciar tablas maestro/detalle, donde no es necesaria la correlatividad, de todas manera es interesante tu forma.


  • 0

#15 giulichajari

giulichajari

    Advanced Member

  • Miembros
  • PipPipPip
  • 477 mensajes

Escrito 29 junio 2015 - 06:09

Yo utilizo mysql y en la universidad me enseñaron a utilizar campos autoincrementales, y eso tuve que  cambiarlo por una tabla identificadores donde le sumo 1 manualmente. y obviamente si falla la insercion de una factura debe anularse los productos asociados a la misma. Esto seria la atomicidad. Pero que sucede si se quieren insertar varias facturas a la vez? Bueno el caso es que el motor de mysql no maneja por si solo esto.. se deben tener procedimientos almacenados...y si queda un id hueco no hace nada ya que un entero de 64 bits es inmenso..

Saludos


  • 0

#16 cram

cram

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 828 mensajes
  • LocationMisiones, Argentina

Escrito 01 julio 2015 - 02:12

La idea es, según no se ve muy bien en el ejemplo, utilizar un generador, pero sin el trigger típicamente asociado al insert.

Creo que Nikolas lo percibió.

Se supone que el generador suma uno, cero o menos uno según el caso. Entonces, utiliza el procedimiento para tomar el valor actual sumando cero, es decir devolviendo el valor actual, y lo lleva a una variable local del programa. Luego, solo y solo cuando se da el commit de la venta se llama al generador con un valor de incremento igual a uno.

Obviamente, esto lo inventé dado que se trata de una solución para una aplicación que funciona sola. Pues, de lo contrario tendría que tener varios geneadores: uno para cada punto de venta.

 

Más allá de esta explicación, en un ambiente normal, siempre es mejor el uso de la tabla. Pero a mi me gusta inventar.

 

Saludos.


  • 0





Etiquetado también con una o más de estas palabras: generador, generator