Ultimo Registro insertado en Firebird

2,338 views
Skip to first unread message

Antonio Meza

unread,
May 9, 2011, 7:55:06 PM5/9/11
to Comunidad de Visual Foxpro en Español
Hola a todos,buena tarde!!

Tengo un detalle, que no se como obtener el ultimo ID insertado o
agregado a una tabla en FireBird, en el caso de mysql solo con hacer
un select de esta forma:

select last_insert_id()

Alguien sabe como hacerlo poro con Firebird?

Saludos
Antonio Meza

Walter R. Ojeda Valiente

unread,
May 9, 2011, 9:01:31 PM5/9/11
to publice...@googlegroups.com
Hay varias formas, por ejemplo:

SELECT MAX(VEN_CODIGO) FROM VENDEDORES;

Si necesitas tener ese valor en una variable, entonces dentro de un stored procedure podrías escribir algo como:

SELECT MAX(VEN_CODIGO) FROM VENDEDORES INTO :UltimoCodigo;

También podrías escribir algo como:

SELECT FIRST 1 VEN_CODIGO FROM VENDEDORES ORDER BY VEN_CODIGO DESC;

Saludos.

Walter.



> Date: Mon, 9 May 2011 16:55:06 -0700
> Subject: [vfp] Ultimo Registro insertado en Firebird
> From: solv...@gmail.com
> To: publice...@googlegroups.com

ArilSoft

unread,
May 10, 2011, 9:11:33 AM5/10/11
to Comunidad de Visual Foxpro en Español
Buen dia

Mejor respuesta que Walter creo que no

Yo iba a agregar que en el ejemplo sql_demo de el mismo viene una
implementacion en un store procedure de como obtener el ultimo id y
que se vuelva la base para que genere el siguiente id disponible para
ti.

Arilsoft
Message has been deleted

Victor Espina

unread,
May 10, 2011, 9:26:29 AM5/10/11
to publice...@googlegroups.com
Utilizar el MAX() para obtener el ultimo valor insertado en un ambiente multiusuario no es viable ni practico. No es practico, pues procesar un MAX() sobre toda una tabla obliga al motor de datos a aplicar un bloqueo de lectura sobre toda la tabla, lo cual puede afectar a otras operaciones que se esten realizando en ese momento sobre esa tabla. 

Tampoco es viable pues si otro usuario inserta un registro en la tabla justo entre el momento en que yo hago mi INSERT y luego el SELECT MAX(), el valor que obtendria seria el del ID del registro insertado por el otro usuario y no el que yo inserte.

Segun esta entrada de la FAQ de Firebird, el equivalente al last_insert_id() de SQL para FireBird seria:

select gen_id(GENERATOR_NAME, 0)
 from rdb$database;


Sin embargo, tal como se indica en el link, esto devuelve el ultimo ID generado, el cual no necesariamente corresponde al ID asignado al INSERT que yo ejecute.  Es por esto que recomiendan obtener el ID de antemano antes de ejecutar el INSERT y pasar el valor directamente a la columna:

declare variable id bigint;
select gen_id(GENERATOR_NAME, 1)
  from rdb$database
   into :id;

INSERT INTO t1 (pk, ...) values (:id, ...);


De esta forma podemos garantizar que el ID que tenemos es el usado en el registro que insertamos. Siguiendo con lo indicado en el articulo, en FireBird 2 esto se puede simplificar usando la nueva clausula RETURNING de INSERT:

INSERT INTO t1 (...) values (...) returning pk;

Espero que la informacion te sea de utilidad.

Saludos

Victor Espina


GeoSys Diseño de Software

unread,
May 10, 2011, 10:54:35 AM5/10/11
to publice...@googlegroups.com
Gracias por los aportes colegas, ya que en esa parte yo también tengo dudas, entonces con esto nos damos cuenta cual fue el último registro el que insertamos en nuestra :


INSERT INTO t1 (...) values (...) returning pk;

Ahora en el caso de llevar consecutivos por ejemplo números de factura y que no se repitan ni que devuelva error a la hora de violar la unicidad, cual sería la mejor manera, con claves primarias, claves foráneas o hay otra forma más efectiva como la anterior.

Por ejemplo con las DBF´s yo lo que hago es bloquear el encabezado de la tabla de consecutivos para que nadie más inserte registros y así es muy fácil asignar e insertar el número de consecutivo que sigue, Esta práctica hace lógicamente lento el proceso en una red de varios usuarios.

En Firebird como se logra esto de forma rápida y efectiva?

el SELECT MAX() ?

O select gen_id(GENERATOR_NAME, 0)
 from rdb$database;

????

Gracias por los comentarios basados en las experiencias de ustedes y que nos puedan ayudar a muchos que como yo estamos viendo la mejor forma de usar el Firebird.

Saludos

Antony Contreras Peralta

Costa Rica.

Victor Espina

unread,
May 10, 2011, 11:07:35 AM5/10/11
to publice...@googlegroups.com
En SQL Server yo lo que hago es crear un SP que hace algo como esto:

DECLARE @numfactura INT
UPDATE Numeracion
       SET @numFactura = numero = numero + 1
 WHERE tipo = 'FA'

RETURN @numFactura


No se si algo como esto sera posible en Firebird.

Saludos

Victor Espina


Antonio Meza

unread,
May 10, 2011, 12:02:13 PM5/10/11
to Comunidad de Visual Foxpro en Español
Muchas gracias por responder a todos!!!

Efectivamente, en Mysql por ejemplo, siempre te retorna el ultimo id
agregado aunque otro usuario este guardando registros nuevos ya que es
por conexion, pero todo lo que eh leido de firebird no lo hace asi, y
el detalle es cuando el sistema es multi-usuario, voy a probar con los
ejemplos que me han dado, lo que estaba haciendo era algo asi, pero
siempre hay cosas mejores!!!

En la tabla tener un campo tipo caracter 15, donde almaceno la
variable IdUsuario + sys(2015) de vfp, de esta forma puedo buscar el
registro nuevo por medio de esta variable que no se va a repetir, ya
que el IDUsuario esta almacenado en la tabla de usuarios y es
diferente a los demas, lo importante es cuidar que el usuario no
ingrese 2 veces y este guardando registros en la misma tabla, porq
podria dar el caso muy remoto que el valor se duplicara, o en su caso
no permitir valores repetidos en este campo.

Gracias a todos yv voy a leer sobre lo que comentan y a probar los
ejemplos!!!

Esto es para la clase que estoy haciendo de DBVFP por medio de
cursores actualizables SPT

Walter R. Ojeda Valiente

unread,
May 10, 2011, 1:56:45 PM5/10/11
to publice...@googlegroups.com
Tienes razón Arilsoft, se me pasó esa, también se podría escribir:
 
SELECT GEN_ID(GEN_PROVEEDOR, 0) FROM RDB$DATABASE;
 
Pero este caso solamente serviría si el ID insertado fue obtenido mediante un generador (en el ejemplo se llama GEN_PROVEEDOR).
 
La función interna GEN_ID() aumenta el valor de su primer argumento en la cantidad especificada en el segundo argumento. Por lo tanto, si le aumentamos 0 (cero) estamos obteniendo el último valor. Pero .... hay un pequeño (gran) problema ¿cuál es? que si el último registro insertado fue posteriormente borrado entonces el valor del último ID que vemos en la tabla no corresponderá al valor obtenido con el SELECT escrito líneas arriba.
 
Por lo tanto, sería mejor hacer un SELECT MAX() ... el cual sí mostrará el último ID de la tabla.
 
Saludos.
 
Walter.
 
 

 
> Date: Tue, 10 May 2011 06:11:33 -0700
> Subject: [vfp] Re: Ultimo Registro insertado en Firebird
> From: arilsof...@gmail.com
> To: publice...@googlegroups.com

Walter R. Ojeda Valiente

unread,
May 10, 2011, 2:04:48 PM5/10/11
to publice...@googlegroups.com
Hola Víctor
 
No, no es el caso de Firebird, el cual usa la arquitectura MGA, y por lo tanto no bloquea la tabla para obtener el MAX(), el MIN(), el AVG(), o lo que sea.
 
Realizar un MAX() es rapidísimo pues al escribir esa función el Firebird automáticamente ordena la tabla en forma descendente y encontrará el valor buscado en la primera fila.
 
Saludos.
 
Walter.
 

 

Date: Tue, 10 May 2011 06:24:15 -0700
From: vesp...@gmail.com
To: publice...@googlegroups.com
Subject: Re: RE: [vfp] Ultimo Registro insertado en Firebird

Utilizar el MAX() para obtener el ultimo valor insertado en un ambiente multiusuario no es viable ni practico. No es practico, pues procesar un MAX() sobre toda una tabla obliga al motor de datos a aplicar un bloqueo de lectura sobre toda la tabla, lo cual puede afectar a otras operaciones que se esten realizando en ese momento sobre esa tabla. En este caso, es mucho mas eficiente usar un TOP + ORDER:

Tampoco es viable pues si otro usuario inserta un registro en la tabla justo entre el momento en que yo hago mi INSERT y luego el SELECT MAX(), el valor que obtendria seria el del ID del registro insertado por el otro usuario y no el que yo inserte.

Segun este link, el equivalente al last_insert_id() de SQL para FireBird seria:

select gen_id(GENERATOR_NAME, 0)
 from rdb$database;


Sin embargo, tal como se indica en el link en la FAQ de Firebird, esto devuelve el ultimo ID generado, el cual no necesariamente corresponde al ID asignado al INSERT que yo ejecute.  Es por esto que recomiendan obtener el ID de antemano antes de ejecutar el INSERT y pasar el valor directamente a la columna:

declare variable id bigint;
select gen_id(GENERATOR_NAME, 1)
  from rdb$database
   into :id;

INSERT INTO t1 (pk, ...) values (:id, ...);


De esta forma podemos garantizar que el ID que tenemos es el usado en el registro que insertamos. Siguiendo con lo indicado en el articulo, en FireBird 2 esto se puede simplificar usando la nueva clausula RETURNING de INSERT:

INSERT INTO t1 (...) values (...) returning pk;

Espero que la informacion te sea de utilidad.

Saludos

Victor Espina

Walter R. Ojeda Valiente

unread,
May 10, 2011, 2:04:52 PM5/10/11
to publice...@googlegroups.com

Victor Espina

unread,
May 10, 2011, 2:14:29 PM5/10/11
to publice...@googlegroups.com
Excelente dato.  Gracias Walter. Y esa tecnologia tambien evita que si alguien hizo un Insert en la tabla entre el momento en que yo hice mi insert y ejecute el SELECT MAX(), la tabla me devuelva el ID del registro insertado por el otro usuario y no el que yo inserte?

Victor

Walter R. Ojeda Valiente

unread,
May 10, 2011, 2:26:27 PM5/10/11
to publice...@googlegroups.com
Hola Antony
 
Para tener números consecutivos, por ejemplo números de Factura lo mejor es que tengas un generador pero que se actualice solamente cuando la operación está confirmada ¿por qué? porque una vez obtenido el valor de un generador este ya no retrocede ni siquiera si haces un ROLLBACK (en realidad sí puedes hacer retroceder el valor de un generador pero se desaconseja fuertemente).
 
Esos números de Factura deberían ser clave primaria o clave única, para asegurarte que jamás se repitan. No tienes que bloquear la tabla ni el encabezado de la tabla, nada de eso ya que en Firebird "los lectores no bloquean a los escritores y los escritores no bloquean a los lectores".
 
Puedes mirar en SQL_DEMO como se obtiene el Código del Proveedor (o del Vendedor, o del Producto), allí se utiliza un generador, mira dentro de los triggers.
 
Saludos.
 
Walter.
 

 

Date: Tue, 10 May 2011 08:54:35 -0600
Subject: Re: [vfp] Re: Ultimo Registro insertado en Firebird
From: geos...@gmail.com
To: publice...@googlegroups.com

Guillermo MDQ

unread,
May 10, 2011, 2:48:31 PM5/10/11
to Comunidad de Visual Foxpro en Español

Aca hay un ejemplo de como crear un consecutivo para numero de factura
y como utilizar la clausula RETURNING en Firebird.

http://netfb.blogspot.com/2009/10/la-clausula-returning-firebird.html

Saludos
Guillermo



On 10 mayo, 15:26, "Walter R. Ojeda Valiente" <w...@hotmail.com>
wrote:
> Hola Antony
>
> Para tener números consecutivos, por ejemplo números de Factura lo mejor es que tengas un generador pero que se actualice solamente cuando la operación está confirmada ¿por qué? porque una vez obtenido el valor de un generador este ya no retrocede ni siquiera si haces un ROLLBACK (en realidad sí puedes hacer retroceder el valor de un generador pero se desaconseja fuertemente).
>
> Esos números de Factura deberían ser clave primaria o clave única, para asegurarte que jamás se repitan. No tienes que bloquear la tabla ni el encabezado de la tabla, nada de eso ya que en Firebird "los lectores no bloquean a los escritores y los escritores no bloquean a los lectores".
>
> Puedes mirar en SQL_DEMO como se obtiene el Código del Proveedor (o del Vendedor, o del Producto), allí se utiliza un generador, mira dentro de los triggers.
>
> Saludos.
>
> Walter.
>
> Date: Tue, 10 May 2011 08:54:35 -0600
> Subject: Re: [vfp] Re: Ultimo Registro insertado en Firebird
> From: geosy...@gmail.com
> To: publice...@googlegroups.com
>
> Gracias por los aportes colegas, ya que en esa parte yo también tengo dudas, entonces con esto nos damos cuenta cual fue el último registro el que insertamos en nuestra :
>
> INSERT INTO t1 (...) values (...) returning pk;
>
> Ahora en el caso de llevar consecutivos por ejemplo números de factura y que no se repitan ni que devuelva error a la hora de violar la unicidad, cual sería la mejor manera, con claves primarias, claves foráneas o hay otra forma más efectiva como la anterior.
>
> Por ejemplo con las DBF´s yo lo que hago es bloquear el encabezado de la tabla de consecutivos para que nadie más inserte registros y así es muy fácil asignar e insertar el número de consecutivo que sigue, Esta práctica hace lógicamente lento el proceso en una red de varios usuarios.
>
> En Firebird como se logra esto de forma rápida y efectiva?
>
> el SELECT MAX() ?
>
> O select gen_id(GENERATOR_NAME, 0)
>  from rdb$database;
> ????
>
> Gracias por los comentarios basados en las experiencias de ustedes y que nos puedan ayudar a muchos que como yo estamos viendo la mejor forma de usar el Firebird.
>
> Saludos
>
> Antony Contreras Peralta
>
> Costa Rica.
>

Walter R. Ojeda Valiente

unread,
May 10, 2011, 2:55:16 PM5/10/11
to publice...@googlegroups.com
Hola Víctor
 
En Firebird todo se maneja con transacciones.
 
Si haces un INSERT y luego un SELECT MAX() dentro de la misma transacción entonces obtendrás el último ID que tú insertaste, sin importar cuantas otras inserciones los demás usuarios hayan hecho.
 
Si haces un SELECT MAX() en una transacción que no tiene un INSERT dentro suyo, entonces obtendrás el último ID insertado y "commitado" por los demás usuarios.
 
Cada transacción es autónoma. Lo que está haciendo Juan no lo sabe María y lo que está haciendo María no lo sabe Juan. Solamente después de los respectivos COMMIT es que las operaciones que ellos realizaron (inserción/borrado/modificación) se vuelven públicas. Y por supuesto cada transacción puede actualizar muchas tablas.
 
Para el caso de necesitar números consecutivos y secuenciales lo mejor es sin dudas utilizar un generador, ya que los valores de los generadores son independientes de las transaccciones, o sea que aunque se ejecute un ROLLBACK el valor del generador no cambiará, no disminuirá.
 
Saludos.
 
Walter.
 

 

Date: Tue, 10 May 2011 11:14:29 -0700

From: vesp...@gmail.com
To: publice...@googlegroups.com
Subject: Re: RE: [vfp] Ultimo Registro insertado en Firebird

Miguel Canchas

unread,
May 10, 2011, 4:02:42 PM5/10/11
to publice...@googlegroups.com
POSTGRESQL hace lo mismo...


MK

-----Mensaje original-----
De: publice...@googlegroups.com [mailto:publice...@googlegroups.com] En nombre de Guillermo MDQ
Enviado el: martes, 10 de mayo de 2011 01:49 p.m.
Para: Comunidad de Visual Foxpro en Español
Asunto: [vfp] Re: Ultimo Registro insertado en Firebird

mapner

unread,
May 11, 2011, 1:55:57 PM5/11/11
to Comunidad de Visual Foxpro en Español
El otro día viendo el programa de Walter vi un campo que es
CURRENT_TRANSACTION que no sabia bien para que era utilizado.
Pues bien se puede usar para resolver este tema del ID asignado.

Una opción sería la siguiente:

***********************************

* abro la conexion en el cliente

m.cStr =
"User=SYSDBA;Password=masterkey;Database=MyDB;DataSource=localhost;
Port=3050;Dialect=3; Charset=NONE;Role=;Connection
lifetime=15;Pooling=true; MinPoolSize=0;MaxPoolSize=50;Packet
Size=8192;ServerType=0;"

m.nCon = SQLSTRINGCONNECT(m.cStr)

SQLSETPROP(m.nCon,"Transactions",2) && pongo la transaction en modo
explicito...

* obtengo el número de transaction de Firebird
SQLExec(m.nCon,"select CURRENT_TRANSACTION from rdb$database",'_aux")
m.CT = _aux.CURRENT_TRANSACTION
USE IN _aux

IF 1=SQLExec(m.nCon,"INSERT INTO MiTabla (Nombre,Saldo,current_trn)
Values (?m.cNombre,?m.nSaldo,CURRENT_TRANSACTION)") .... && hago el
INSERT...

* suponiendo que el ID no lo paso por que se asigna en forma
automática via un TRIGGER con un Generator
* en la tabla debo tener un campo que sea current_trn integer (debe
estar indexado por perfomance) y le grabo el valor CURRENT_TRANSACTION
* luego desde el cliente sabemos la conexión "viva" que se está
utilizando hacemos:

IF 1=SQLCOMMITI(m.nCon)
SQLExec(m.nCon,"select max(id) as id from MiTabla where
current_trn = ?m.CT","_aux")
MESSAGEBOX("El ID es "+TRANSFORM(_aux.id))
USE IN _aux
ENDIF
ELSE
SQLROLLBACK(m.nCon)
ENDIF

ENDIF

* Cierro la conexion
SQLDISCONNECT(m.nCon)

***********************************


saludos

mapner

unread,
May 11, 2011, 2:08:16 PM5/11/11
to Comunidad de Visual Foxpro en Español
A partir de Firebird 2.0

http://www.firebirdfaq.org/faq243/

saludos

On 9 mayo, 20:55, Antonio Meza <solver...@gmail.com> wrote:
Reply all
Reply to author
Forward
0 new messages