Mysql - Cómo averiguar el último registro insertado en una tabla

Hay veces que realizamos operaciones de inserción que involucran valores autonuméricos y necesitamos averiguar el idetificador del último registro que hemos añadido para utilizarlo en verificaciones, o incluso en otras operaciones con la base de datos, como insertar registros hijos.

La función a utilizar en este caso sería last_insert_id(), a continuación muestro un ejemplo de su funcionamiento

He establecido un entorno de pruebas muy sencillo con un pequeño esquema que representa un sistema de autobuses y sus viajeros.

La tabla autobuses registraría las diferentes líneas entre dos ciudades. Para identificar los autobuses se utiliza una columna de tipo AUTO_INCREMENT que aumentará de valor automáticamente sin que tengamos que especificarla. Al crear una columna de este tipo, es obligatorio que se use en una clave, así que la usaremos como clave primaria.

CREATE TABLE autobuses(
id_bus int not null AUTO_INCREMENT,
ciudad_origen text not null,
ciudad_destino text not null,
PRIMARY KEY(id_bus)
);

La tabla de viajeros registraría los viajeros que pueden utilizar el servicio, se utiliza un identificador de viajero del mismo tipo que el identificador del autobus

CREATE TABLE viajeros(
id_viajero int not null AUTO_INCREMENT,
nombre text not null,
apellidos text not null,
PRIMARY KEY(id_viajero)
);

Por último necesitamos una tabla que referencie los autobuses con los viajeros en una fecha, es decir que controle las fechas y las líneas que utiliza cada viajero.

CREATE TABLE autobuses_viajeros(
id_bus int not null,
id_viajero int not null,
fecha date not null
);

Damos de alta un autobús realizando una operación de inserción (omitimos el campo de identificación del autobús porque se rellena de forma automática con la columna de tipo AUTO_INCREMENT)

mysql> insert into autobuses (ciudad_origen, ciudad_destino)
VALUES ('Barcelona', 'Zaragoza');
Query OK, 1 row affected (0.02 sec)

Podemos recuperar el valor del último registro generado con last_insert_id()

mysql> select last_insert_id();
+------------------+
| last_insert_id() |
+------------------+
| 1 |
+------------------+
1 row in set (0.00 sec)

Podemos dar de alta viajeros insertándolos, cada vez que insertemos un registro que involucra a una columna de tipo AUTO_INCREMENT o autonumérica, el valor de last_insert_id() varía, es decir que se genera cada vez que se llama a un autonumérico a nivel de sesión.

mysql> insert into viajeros (nombre,apellidos)
VALUES ('Juan', 'Pérez');
Query OK, 1 row affected (0.01 sec)
mysql> select last_insert_id();
+------------------+
| last_insert_id() |
+------------------+
| 1 |
+------------------+
1 row in set (0.00 sec)
mysql> insert into viajeros (nombre,apellidos)
VALUES ('Manuel', 'Fernández');
Query OK, 1 row affected (0.02 sec)
mysql> select last_insert_id();
+------------------+
| last_insert_id() |
+------------------+
| 2 |
+------------------+
1 row in set (0.00 sec)
mysql> insert into viajeros (nombre,apellidos)
VALUES ('María', 'López');
Query OK, 1 row affected (0.02 sec)
mysql> select last_insert_id();
+------------------+
| last_insert_id() |
+------------------+
| 3 |
+------------------+
1 row in set (0.00 sec)

Debo aclarar también que el valor de last_insert_id() es privado para la sesión que ejecuta las llamadas a columnas de tipo AUTO_INCREMENT, nadie más excepto nuestra sesión tendrá acceso al identificador de registro generado.

El valor recuperado por last_insert_id() se puede utilizar normalmente donde prodría ir una expresión, por ejemplo una sentencia de inserción del último viajero en una línea de bus determinada.

mysql> insert into viajeros (nombre,apellidos)
VALUES ('Rosa', 'Álvarez');
Query OK, 1 row affected (0.02 sec)
mysql> select last_insert_id();
+------------------+
| last_insert_id() |
+------------------+
| 4 |
+------------------+
1 row in set (0.00 sec)
mysql> insert into autobuses_viajeros
(id_bus, id_viajero, fecha)
VALUES (1, last_insert_id(), now());
Query OK, 1 row affected (0.02 sec)
mysql> select * from autobuses_viajeros;
+--------+------------+------------+
| id_bus | id_viajero | fecha |
+--------+------------+------------+
| 1 | 4 | 2008-04-30 |
+--------+------------+------------+
1 row in set (0.00 sec)
mysql> select last_insert_id();
+------------------+
| last_insert_id() |
+------------------+
| 4 |
+------------------+
1 row in set (0.00 sec)

En este caso podemos comprobar que se utiliza correctamente el valor de last_insert_id() para la inserción en otra tabla y que esa inserción en una tabla que no contiene valores AUTO_INCREMENT no hace que el valor de last_insert_id() varíe.

Nota: La función now() devuelve la fecha y hora actuales, como el campo es de tipo date, solamente almacena la fecha.

Pero vamos a rizar un poco más el asunto, ¿qué ocurre cuándo se utiliza last_insert_id() en un insert múltiple?, es decir, cuando se insertan varios registros en una sola sentencia:

mysql> insert into viajeros (nombre,apellidos)
VALUES ('Ramón', 'Sánchez'),('Sara','Hernández'),
('Julián','Martínez');
Query OK, 3 rows affected (0.03 sec)
Records: 3 Duplicates: 0 Warnings: 0
mysql> select last_insert_id();
+------------------+
| last_insert_id() |
+------------------+
| 5 |
+------------------+
1 row in set (0.00 sec)

Mysql retornará el valor generado por el primer registro insertado, pero no por el resto. Si hubieramos insertado los 3 viajeros en 3 inserts diferentes, hubieramos obtenido 5, 6 y 7 como valores para last_insert_id().

Espero haber aclarado un poco más sobre el funcionamiento de last_insert_id() con este caso práctico.

Comentarios

chido la explicacion, me sirvio de mucho ya que stoy haciendo una base de datos y no sabia como relacionarla de esa manera.
GRACIAS

No hay de que, me alegro mucho que te haya sido de utilidad.

De 10 la explicacion.... Mil gracias.
http://lalosoft.com.ar

Muchas gracias Lalo.

Es recomendable para una mejor consistencia de los datos en las inserciones multiples utilizar Lock Table?.

Has de ser cuidadoso con las sentencias LOCK TABLE, ya que estas bloquean toda la tabla y nadie más podria modificar o borrar datos de la misma hasta que la operativa concluya y se desbloquee.

Los datos te van a resultar consistentes, en mi opinión, si utilizas auto increment para la clave primaria, no tendras problemas, y si utilizas campos lógicos que inserten valores diferentes para una misma clave, el problema es de la aplicación...

Para acelerar el proceso de inserción masivo te recomiendo que elimines los índices de la tabla a insertar y los crees de nuevo al final del proceso. Otra opción es utilizar INSERT DELAYED, que inserta los registros agrupados en bloques y con una gestión de cola, pero no irá con tablas InnoDB.

Espero haber resuelto tu duda, gracias por comentar!

Muchas Gracias pero desafortunadamente eso funciona en modo consola pero en MySQL Query Browser no funciona la función last_insert_id() alguien sabe por qué?

[...] 2. Mysql - Cómo averiguar el último registro insertado en una tabla [...]

[...] Mysql - Cómo averiguar el último registro insertado en una tabla (3.457 visitas) [...]

Estoy intentando resolverlo pero... ¿es posible que LAST_INSERT_ID devuelva un BIGINT independientmente del tipo de datos que sea el campo autonumérico que ha "sufrido" la última inserción?

Pues sí, así es. En una tabla con autonumérico definido como MEDIUMINT(8) UNSIGNED la función LAST_INSERT_ID devuelve un valor de tipo BIGINT (además signed me parece). Curioso...

¿Cómo has hecho esta comprobación Albert? Sería muy interesante que lo explicaras :)

La verdad es que lo he intentado comprobar desde dentro del propio MySQL y no he podido...

El problema en realidad lo tenía desde .NET. Tal como expliqué en su día en <a href="http://www.albertmata.net/2008/11/correspondencia-entre-tipos-de-datos-e... post</a> existe una equivalencia entre los tipos de datos que MySQL devuelve y los que .NET recepciona (por así decirlo). Hasta ahora todo me ha ido de fábula, pero ahora me ha dado por utilizar un LAST_INSERT_ID() sobre un autonumérico definido como MEDIUMINT(8) UNSIGNED y claro, intentaba recepcionarlo como UInt32 que es como debo recepcionar cualquier dato de ese tipo concreto en MySQL. Y obtenía excepciones de casting hasta que me ha dado por comenzar a hacer pruebas y he descubierto que esté creado el autonumérico como quiera, el LAST_INSERT_ID siempre tengo que recepcionarlo como Int64 (el que habitualmente utilizo para recepcionar BIGINT de MySQL). De ahí he deducido que dicha función de MySQL devuelve en realidad un BIGINT.

Digamos que más que deductivo ha sido empírico el tema...

Debo haber puesto mal el link, ¿puedes corregirlo?

http://www.albertmata.net/2008/11/correspondencia-entre-tipos-de-datos-e...

Corregido!

¿no es posible que la conversion la haga .NET o el ODBC directamente? me parece muy raro y la documentación no dice nada al respecto...

Sí, lo he pensado que la conversión la hiciera el conector de .NET (no uso ODBC), pero los bugs parecen ir en la linea de que realmente dicha función devuelve un BIGINT "signed". Tampoco pondría la mano en el fuego, está claro...

Curioso porque si hago esto, (con tiny int unsigned), no consigo reproducirlo...

<code>
mysql> create table t1 (id tinyint AUTO_INCREMENT, primary key(id));
mysql> create table t2 (id tinyint);
mysql> insert into t1 values(null);
mysql> select last_insert_id();
+------------------+
| last_insert_id() |
+------------------+
| 1 |
+------------------+
1 row in set (0,00 sec)

mysql> insert into t1 VALUES(126);
Query OK, 1 row affected (0,00 sec)

mysql> insert into t1 VALUES(null);
Query OK, 1 row affected (0,00 sec)

mysql> select last_insert_id();
+------------------+
| last_insert_id() |
+------------------+
| 127 |
+------------------+
1 row in set (0,00 sec)

mysql> INSERT INTO t2 SELECT last_insert_id();
Query OK, 1 row affected (0,00 sec)
Records: 1 Duplicates: 0 Warnings: 0

mysql> select * from t2;
+------+
| id |
+------+
| 127 |
+------+

</code>

Pero fíjate que si haces...

<code>
mysql> create table t1 (id bigint unsigned AUTO_INCREMENT, primary key(id));
Query OK, 0 rows affected (0.06 sec)
mysql> insert into t1 values(null);
Query OK, 1 row affected (0.00 sec)
mysql> insert into t1 VALUES(9223372036854775807);
Query OK, 1 row affected (0.00 sec)
mysql> insert into t1 values(null);
Query OK, 1 row affected (0.00 sec)
mysql> select last_insert_id();
+----------------------+
| last_insert_id() |
+----------------------+
| -9223372036854775808 |
+----------------------+
1 row in set (0.00 sec)
mysql> select * from t1;
+---------------------+
| id |
+---------------------+
| 1 |
| 9223372036854775807 |
| 9223372036854775808 |
+---------------------+
3 rows in set (0.00 sec)
</code>

..."parece" confirmar que el LAST_INSERT_ID se comporta como un BIGING SIGNED, ¿no?

Yo juraría que cerré bien la etiqueta CODE... :-s (¿puedes arreglarlo una vez más?)

Tienes razon, con bigint unsigned se reproduce, pero con tinyint funciona bien... ¿lo has probado para tu caso con mediumint?

No sé, es como si realmente LAST_INSERT_ID devolviera un BIGINT pero no muy tipado :-s, o sea... sí por rango que acepta pero no con el tipo de dato, no sé si me explico. Pero desde .NET ya te digo... o lo recoges con un Int64 (el que debes usar para los BIGINT "SIGNED") u obtienes excepción.

Añado un detalle más al respecto... el conector entre MySQL i .NET que proporciona MySQL genera una serie de objetos, uno de ellos un tal MySqlCommand. Pues bien, acabo de descubrir que este objeto tiene una propiedad de solo lectura llamada LastInsertedId que devuelve un valor de tipo Long (tipo Long es el "equivalente" al BIGINT "SIGNED"), así que parece que más o menos MySQL sí que viene a "admitir" que LAST_INSERT_ID devuelve un Long/BIGINT.

Hola me fue de utilidad, nesecito saber si puedieras ayudarme con esto: quiero que en mi web haya varias secciones que tengan noticias en php como un cms
pero en la pagina principal necesito que solo aparesca 1 noticia de cada seccion como si fuera revista solo quiero una nota de cada seccion para que aparesca lo mas relevante y al dar click se vaya a su seccion en otra pagina es posible?

'Y sin embargo se mueve'. He probado todo: 'last_insert_id()', y otras expresiones similares directamente sobre la base de datos en MySQL y NO FUNCIONAN. Contesta un error y remite al manual: '#1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'last_insert_id()' at line 1'.
Por todo ello, si hay alguien que lo ha probado y la sentencia esté funcionando, le ruego me lo indique y cómo.
Un saludo.

<p>vrotero, esta prueba acabo de hacerla ahora mismo en mi server local:</p>
<code>
mysql> insert into t (g,f) values (5,6);
Query OK, 1 row affected (0.00 sec)

mysql> select last_insert_id();
+------------------+
| last_insert_id() |
+------------------+
| 51 |
+------------------+
1 row in set (0.00 sec)
</code>
<p>¿Qué versión de MySQL estás utilizando? Revisa las especificaciones de last_insert_id en la documentación oficial de mysql: http://dev.mysql.com/doc/refman/5.0/en/information-functions.html#functi...

¡Hola! Muchísimas gracias por ser tan diligente en la respuesta. La cuestión es que en MySQL me funcionaba perfectamente si y sólo si last_insert_id() iba inmediatamente después de la sentencia INSERT o UPDATE.
En cambio en PHP no me funcionaba, pero revisado el código, he conseguido solucionar el problema, utilizando insert_id creo que como un objeto, aunque no estoy muy seguro. El caso es que ya funciona, creo.
Muchas gracias y un saludo desde Asturias.

Gracias por el aporte!!!

Segun dices, last_insert_id() funciona solo para la sesion actual, a esto me surje una duda.

Si hay concurrencia y en tiempos muy cercanos se insertan dos registros en la misma tabla y para cada insert necesitas obtener su id para insertar en otra tabla usando ese valor, hay la poisibilidad de que se pierda el valor deseado en last_insert_id() para cada insert realizado?

Estoy usando procedimientos almacenados y en varias ocasiones necesito obtener el ultimo registro insertado pero hay la posibilidad de que diferentes personas (computadoras) realicen registros en tiempos similares o muy cercanos.

Me gustaria pensar que ahi funciona lo que mencionas de las sesiones pero la verdad no tengo idea si sea necesario usar transacciones dentro del procedimiento almacenado.

Muy bien explicado, más fácil o más claro, imposible!!! gracias!!

holas man muy buen tutorial pero como seria por ejemplo las tablas alumno,matricula,cursos..con relacion de muchos a muchos table intermedia seria la de matricula.....y tabla alumno con id autoincremento y la tabla curso con id autoincremento y.. se deben guardar sus ids en la tabla matricula y ademas un alumno puede tener varios cursos.. estuve trantado de hacerlo pero nu me funcionaba seria de gran ayuda q me dieras una mano gracias

Bien por el manual, pero que pasa si quiero saber de entrada en una tabla cual seria el ultimo id insertado o el próximo en realidad, por que un count en el caso de a ver borrado una linea no me serviría. Pero claro hablo de una nueva conexion, lo primero que quiero saber es el siguiente id que toca antes de insertarlo.

Alguna forma de averiguarlo ?

Saludos y gracias!
http://www.hoteles-en-calafate.com.ar

Hola, alguien sabe porque no me funciona esta funcion last_insert_id() ?, desde el browser de las mysql guitools no me funciona y por código en php tampoco, estoy utilizando un mysql 5.1.54

un saludo.

Quiero ingresar el id de un insert en otra tabla ya que estan relacionadas, pero si lo hago por consola sql funciona pero si lo hago desde php no funciona aqui les dejo el codigo

<?php
$ingresar = @mysql_query('INSERT INTO empresa (rif, nombre,direccion,email) values ("j123456789","consultores xxx","gdipaolo59@gmail.com);

$ultimo_id= mysql_query("select last_insert_id() empresa");

$ingresadatos = @mysql_query (("insert into datospersonales (id_empresa,nombrepersonal) values ".$ultimo_id,"miguel"));
//o de esta manera

$ingresadatos = @mysql_query (("insert into datospersonales (id_empresa,nombrepersonal) values "last_insert_id(),"miguel"));

if($meter== true and $ultimo_id==true and $ingresadatos==true)
{

echo $ultimo_id;
echo "Su registro fue un exito";

else{
echo "Registro invalido";
}
}
?>

HOLA AMIGO GRACIAS POR CREAR ESTE POST NOS AYUDAS MUCHO ------MI REQUERIMIENTO ES EL SIGUIENTE .......COMO HARIA PARA REGISTRAR 1 REGISTRO EN 2 TABLAS QUE ESTEN RELACIONADAS POR EJEMPLO YO TENGO ESTA TABLA
Personal(
IdPersonal int autoicrementable;
Nombre varchar(45),
apellidos varchar(45,
edad int,.....ok
)
---
mi otra tabla es la siguiente
UsuarioPersonal(
IdPersonal int; -----se sabes que es autoincrementable
Nick char(8);
clave char(6);
)

ahora el procedimiento alamcenado ------------tengo la idea de insertar un personal
create procedure sp_ingresopersonal(
in _Nombre varchar(45),
in _apellidos varchar(45),
in edad int
)
begin
insert into personal(Nombre,apellidos,edad)values(_Nombre,_Apellidos,_edad);
end
------------------------hasta ahii me sale ok pero solo el insert al personal ----------------------
ahora Mi pregunta es COMO INGRESO A LA TABLA USUARIO COMO CAPTURO EL ID QUE HE REGISTRADO DEL PERSONAL Y DESPUES HAGO EL INSERT A LA TABLA USUARIO ?------------------------------------------------

ESPERO TU RESPUESTA ES URGENTE LO NESECITO Y MIL GRACIAS POR EL TIEMPO TOMADO EN ESTE PROBLEMAS ANTE MANO MUCHAS GRACIAS AMIGO Y SIGUE PA DELANTE .........ESPERO TU RESPUESTA

Gracias men me sirvio de mucho se agradece todo lo valioso y mas que nada el invertirle tiempo y los conocimientos Saludos

Muy bien aclarado el tema Gracias me sirvio!

Para hacer uso del último Id como desees debes hacer una consulta simple de sql: select * from mitabla order by id DESC; si hacen un echo $varibale['id']; verán que es el último que se muestra; si desean póngalo a prueba.

I have worn ladies panties before my teens, reason is they're more
comfortable then mens underwear.

Añadir nuevo comentario