ID de asignación automática de rieles que ya existe

93

Creo un nuevo registro así:

truck = Truck.create(:name=>name, :user_id=>2)

Mi base de datos tiene actualmente varios miles de entidades para camiones, pero asigné las identificaciones a varias de ellas, de una manera que dejó algunas identificaciones disponibles. Entonces, lo que está sucediendo es que los rieles crean un elemento con id = 150 y funciona bien. Pero luego intenta crear un elemento y asignarle id = 151, pero es posible que ese id ya exista, así que veo este error:

ActiveRecord::RecordNotUnique (PG::Error: ERROR: duplicate key value violates unique constraint "companies_pkey" DETAIL: Key (id)=(151) already exists.

Y la próxima vez que ejecute la acción, simplemente asignará el id 152, que funcionará bien si ese valor aún no se ha tomado. ¿Cómo puedo hacer que los rieles comprueben si ya existe un ID antes de que lo asigne?

¡Gracias!

EDITAR

La identificación del camión es lo que se está duplicando. El usuario ya existe y es una constante en este caso. En realidad, es un problema heredado con el que tengo que lidiar. Una opción es volver a crear la tabla y dejar que los rieles asignen automáticamente cada identificación esta vez. Empiezo a pensar que esta puede ser la mejor opción porque tengo algunos otros problemas, pero la migración para hacer esto sería muy complicada porque Truck es una clave externa en muchas otras tablas. ¿Habría una manera sencilla de que rieles crearan una nueva tabla con los mismos datos que ya están almacenados en Truck, con ID asignados automáticamente y manteniendo todas las relaciones existentes?

D-Niza
fuente
¿Por qué no permite que los rieles asignen la identificación automáticamente? Eso eliminaría cualquier peligro de duplicación, ¿o se trata de un problema de datos heredados en el que debe conservar las identificaciones antiguas? Solo quiero entender un poco el caso comercial, ya que esto no es lo habitual al crear un nuevo objeto.
MBHNYC
@MBHNYC Creo que D-Nice está asignando un user_id al crear la empresa, y no un id como estás pensando (y yo también lo hice por un momento).
Anil
Ooo bien atrapa a Anil, tienes toda la razón. @ D-Bien, tal vez agregue su migración para esta tabla a su publicación en caso de que haya algo extraño. Tentador editar ese user_id para eliminar la confusión ..
MBHNYC
No, lo siento, no estaba claro, la identificación de la empresa es lo que se está duplicando. El usuario ya existe y es una constante en este caso. En realidad, es un problema heredado con el que tengo que lidiar. Editar publicación con más información
D-Nice
No es necesario volver a crear la tabla. Solo vea mi respuesta para restablecer la secuencia.
Dondi Michael Stroma

Respuestas:

87

Probablemente Rails esté usando la secuencia incorporada de PostgreSQL. La idea de una secuencia es que solo se usa una vez.

La solución más simple es establecer la secuencia de la columna company.id en el valor más alto de la tabla con una consulta como esta:

SELECT setval('company_id_seq', (SELECT max(id) FROM company));

Supongo que el nombre de la secuencia "company_id_seq", el nombre de la tabla "empresa" y el nombre de la columna "id" ... reemplácelos por los correctos. Puede obtener el nombre de la secuencia con SELECT pg_get_serial_sequence('tablename', 'columname');o mirar la definición de la tabla con \d tablename.

Una solución alternativa es anular el método save () en la clase de su empresa para configurar manualmente la identificación de la empresa para las nuevas filas antes de guardar.

Dondi Michael Stroma
fuente
Supongo que lo que haría es que la asignación automática comience con lo que actualmente es el valor más alto + 1.
D-Niza
Creo que esta es la mejor respuesta a mi pregunta, sin embargo, por razones no relacionadas, tendré que encontrar una manera de usar la estrategia que describí en mi edición de OP
D-Nice
No entiendo por qué sucedió esto para empezar. Esto me ha sucedido a mí y me gustaría entender cómo es posible.
Hunt Burdick
2
@Websitescenes, si uno tiene una columna SERIAL en PostgreSQL (una columna serial es aquella en la que el valor predeterminado es el siguiente valor en una secuencia), luego llena la tabla con valores duros en esa columna, la secuencia no se actualizará automáticamente. Ejemplo: create table t (id serial not null primary key); insert into t values (1); insert into t values (default); ERROR: duplicate key value violates unique constraint "t_pkey" DETAIL: Key (id)=(1) already exists.
Dondi Michael Stroma
207

Hice esto que me resolvió el problema.

ActiveRecord::Base.connection.tables.each do |t|
  ActiveRecord::Base.connection.reset_pk_sequence!(t)
end

¡Encontré el reset_pk_sequence! de este hilo. http://www.ruby-forum.com/topic/64428

Una tarta
fuente
4
Gracias, la mejor solución. Después de la transferencia de la base de datos tuve el mismo problema.
Oleg Pasko
63
O el equivalente de una sola línea (para copiar / pegar en la consola de rieles):ActiveRecord::Base.connection.tables.each { |t| ActiveRecord::Base.connection.reset_pk_sequence!(t) }
Raf
¿Alguna idea de cómo se desincroniza esto?
Tall Paul
25

Basado en la respuesta de @Apie .

Puede realizar una tarea y ejecutarla cuando lo necesite con:

rake database:correction_seq_id

Creas tareas como esta:

rails g task database correction_seq_id

Y en el archivo generado ( lib/tasks/database.rake) poner:

namespace :database do
    desc "Correction of sequences id"
    task correction_seq_id: :environment do
        ActiveRecord::Base.connection.tables.each do |t|
            ActiveRecord::Base.connection.reset_pk_sequence!(t)
        end
    end
end
inyectar
fuente
4

Me suena a un problema de base de datos y no a un problema de Rails. ¿Es posible que su base de datos tenga una semilla de identidad incorrecta en su idcolumna? Para probar, intente hacer un par de inserciones directamente en su base de datos y vea si existe el mismo comportamiento.

mynameiscoffey
fuente
3
¿Por qué el voto negativo? Este es el comportamiento exacto que ocurre si configura su secuencia de incremento en algo que sea más bajo que otros valores existentes y, por lo tanto, ocasionalmente choca con colisiones al insertar datos. El cartel ya dijo que hay datos existentes que entran en este caso.
mynameiscoffey
Puedo insertar bien. Después de recibir este error, puedo ejecutar la misma acción nuevamente y hacer que funcione, si la siguiente identificación en la secuencia aún no se tomó.
D-Niza
Parece que esta era mi situación: me encontré con el problema, pero el siguiente registro que inserté funcionó bien, por lo que debe haber colocado la semilla en el lugar correcto.
Ben Wheeler
3

Resolví este problema siguiendo el comando.

Ejecuta esto en tu consola de rieles

ActiveRecord::Base.connection.reset_pk_sequence!('table_name')
Jigar Bhatt
fuente