2010-05-03 7 views
29

¿Hay alguna forma compacta con ActiveRecord para consultar qué ID va a utilizar a continuación si un objeto iba a persistir en la base de datos? En SQL de una consulta como esta sería algo como:Ruby on Rails: ¿Cómo hacer que ActiveRecord muestre la próxima identificación (última + 1)?

SELECT max(id) + 1 FROM some_table; 
+1

Esto depende de la base de datos que está utilizando, y si los registros se han eliminado .....EJEMPLO: borre los últimos 1000 registros en una tabla postgresql y haga una inserción. La identificación no es el máximo + 1. En una paradoja DB, el próximo registro podría obtener una identificación anterior, porque la reutiliza. Realmente realmente depende de tu DB. – baash05

+0

Además ... si tiene dos personas conectadas y ambas ingresan productos/compras_ordenes/trabajos/eventos. Esto devolvería la misma identificación para ambos. – baash05

+2

La respuesta simple es que no lo haces así, creas un modelo generador/proveedor de identificación. – ocodo

Respuesta

-37

que esto debería funcionar:

YourModel.last.id + 1 
+3

¿Qué pasaría si hubiera ejecutado 'YourModel.last.delete', y luego' YourModel.last.id + 1'? ¿No devolvería eso un número uno menos que la próxima identificación? – BananaNeil

+2

No funciona si su tabla está vacía y la siguiente clave principal no es 1. – jspooner

+7

Esta no es una buena respuesta. No solo no funciona para los casos antes mencionados, no funciona en un entorno altamente transaccional con numerosas inserciones. @randombits, ¿quizás quieres cambiar la respuesta aceptada para esta pregunta para no dirigir a las personas en la dirección incorrecta? – WattsInABox

29

Si bien se acepta fig's answer puede ser que quiera llamar su atención sobre una cosa pequeña. Si está obteniendo el próximo ID para establecerlo en un registro en particular antes de guardarlo, creo que no es una buena idea.

porque como un ejemplo en un sistema basado en web

  1. usted consigue el último ID como 10
  2. se establece el siguiente ID como 11
  3. antes de guardar el registro de otra persona ha salvado el registro , ahora el último número de identificación debe ser de 12 .. del mismo modo

no estoy seguro de que desea el último id para hacer lo que estoy pensando aquí, pero si éste es tan sólo para llamar su atención.

+0

tal vez debería obtenerlo after_save –

1

No creo que haya una respuesta genérica a esto, ya que es posible que no desee asumir una base de datos sobre otra. Oracle, por ejemplo, puede asignar identificadores por una secuencia o, peor aún, por un disparador.

Por lo que se refiere a otras bases de datos, uno puede configurarlas con asignación de identificación no secuencial o aleatoria.

¿Puedo preguntar cuál es el caso de uso? ¿Por qué necesitarías anticipar la próxima identificación? ¿Qué significa "siguiente"? En relación a cuándo? ¿Qué pasa con las condiciones de carrera (entornos multiusuario)?

12

Ligeramente mejor que la respuesta aceptada:

YourModel.maximum(:id) + 1 

Aún tendencia a acelerar las condiciones, etc., pero al menos va a tomar nota de los identificadores omitidos y es ligeramente más eficiente que, por ejemplo, ordenar la tabla por id luego devolviendo el último.

+0

esto no es confiable. Si el modelo con la identificación más alta (n) se ha eliminado, esta solución devolverá el valor n-1 + 1, o simplemente n. Pero la siguiente identificación que se asignará es en realidad n + 1. –

+0

Sí, como se señaló, esto no es perfecto, simplemente mejor que la respuesta aceptada :) Devolverá consistentemente una identificación que no se toma. –

+1

Dado que 'YourModel.maximum (: id)' devuelve 'nil' para una tabla vacía, es posible que desee:' (YourModel.maximum (: id) || 0) + 1'. Es poco probable que sea un problema en un sistema de producción establecido, pero podría ser más robusto en un entorno nuevo o de prueba. –

65

Aquí se modifica ligeramente Taryn East 's versión:

Model.maximum(:id).next 
+2

En mi humilde opinión Puede proporcionar esto como un comentario a Taryn's anwer – Micha

+1

No tengo suficientes votos/puntos para hacer comentarios :( – kimerseen

+0

:-) Ok, lo siento. Tómelo como una recomendación para el tiempo con suficientes rep-points. – Micha

1

Parece que hay necesidad de una respuesta aquí que elimina las condiciones de carrera, y aborda el problema real de pre-proporcionar identificadores únicos.

Para resolver el problema, usted mantiene un segundo modelo, que sirve identificadores únicos para el modelo primario. De esta manera no tienes ninguna condición de carrera.

Si necesita hacer estas identificaciones seguras, debe hash con SHA256 (o SHA512) y almacenar el hash como una columna indexada en el modelo de identificador cuando se generan.

Luego se pueden asociar y validar cuando se utilizan en el modelo principal. Si no los hasch, todavía los asocias para proporcionar validación.

Voy a publicar un código de ejemplo un poco más tarde.

2

Si quiere estar seguro de que nadie más puede tomar el 'nuevo' índice, debe bloquear la tabla. Al utilizar algo como:

ActiveRecord::Base.connection.execute("LOCK TABLES table_name WRITE") 

y

ActiveRecord::Base.connection.execute("UNLOCK TABLES") 

Pero esto es específico para cada motor de base de datos.

La única respuesta correcta para una columna ID secuencial es:

YourModel.maximum(:id)+1 

Si ordena su modelo en un ámbito predeterminado, apellido y dependerá de ese orden.

+0

En tablas vacías 'YourModel.maximum (: id)' dará nil. Entonces 'nil + 1' dará error – user566245

1

Si nadie más está usando la tabla (de lo contrario tendría que utilizar el bloqueo), se puede obtener el valor de incremento automático de MySQL, el comando SQL es

SELECT auto_increment FROM information_schema.tables 
WHERE table_schema = 'db_name' AND table_name = 'table_name'; 

y el comando Carriles sería

ActiveRecord::Base.connection.execute("SELECT auto_increment 
    FROM information_schema.tables 
    WHERE table_schema = 'db_name' AND table_name = 'table_name';").first[0] 
1

Obtener el más grande de ID en la tabla

YourModel.maximum(:id)

Esto ejecutará el siguiente código SQL

SELECT MAX("your_models"."id") AS max_id FROM "your_models"

convertir el resultado en un número entero llamando to_i. Esto es importante ya que para una tabla vacía, el comando max anterior devolverá nil. Afortunadamente nil.to_i devuelve 0.

Ahora para obtener el siguiente ID disponible, sólo tiene que añadir 1 + 1`

El resultado final:

YourModal.maximum(:id).to_i+1 
9

Si su base de datos es PostgreSQL, se puede obtener la siguiente id con esto (por ejemplo, una tabla llamada 'usuarios'):

ActiveRecord::Base.connection.execute("select last_value from users_id_seq").first["last_value"] 

a diferencia de las otras respuestas, este valor no se ve afectada por la supresión de la re cuerdas.

Probablemente haya un equivalente mySQL, pero no tengo una configuración para confirmar.

2

Ésta es una vieja pregunta, pero ninguna de las otras respuestas funciona si ha eliminado el último registro:

Model.last.id #=> 10 
Model.last.destroy 
Model.last.id #=> 9, so (Model.last.id + 1) would be 10... but... 
Model.create #=> 11, your next id was actually 11 

He resuelto el problema utilizando el siguiente enfoque:

current_value = ActiveRecord::Base.connection.execute("SELECT currval('models_id_seq')").first['currval'].to_i 
Model.last.id #=> 10 
Model.last.destroy 
Model.last.id #=> 9 
current_value + 1 #=> 11 
0

Don' t obtener la intención, pero es posible que desee pensar en utilizar GUID

Cuestiones relacionadas