ID d'attribution automatique de rails qui existe déjà

92

Je crée un nouveau disque comme ceci:

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

Ma base de données contient actuellement plusieurs milliers d'entités pour les camions, mais j'ai attribué les identifiants à plusieurs d'entre eux, de manière à laisser certains identifiants disponibles. Donc, ce qui se passe, c'est que les rails créent un élément avec id = 150 et cela fonctionne bien. Mais ensuite, il essaie de créer un élément et de lui attribuer id = 151, mais cet identifiant peut déjà exister, donc je vois cette erreur:

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

Et la prochaine fois que j'exécuterai l'action, elle attribuera simplement l'id 152, ce qui fonctionnera bien si cette valeur n'est pas déjà prise. Comment puis-je obtenir des rails pour vérifier si un identifiant existe déjà avant de l'attribuer?

Merci!

ÉDITER

L'identifiant du camion est ce qui est dupliqué. L'utilisateur existe déjà et est une constante dans ce cas. C'est en fait un problème hérité que je dois régler. Une option consiste à recréer la table en laissant les rails attribuer automatiquement chaque id cette fois-ci. Je commence à penser que c'est peut-être le meilleur choix car j'ai quelques autres problèmes, mais la migration pour ce faire serait très compliquée car Truck est une clé étrangère dans tant d'autres tables. Y aurait-il un moyen simple de faire en sorte que les rails créent une nouvelle table avec les mêmes données déjà stockées sous Camion, avec des ID attribués automatiquement et en conservant toutes les relations existantes?

D-Nice
la source
pourquoi ne laissez-vous pas les rails attribuer automatiquement l'ID? Cela éliminerait tout risque de duplication - ou s'agit-il d'un problème de données héritées où vous devez conserver les anciens identifiants? Je veux juste comprendre un peu l'analyse de rentabilisation, car ce n'est pas le cas lors de la création d'un nouvel objet.
MBHNYC
@MBHNYC Je pense que D-Nice attribue un user_id lors de la création de l'entreprise, et non un id comme vous le pensez (et je l'ai fait pendant un moment aussi).
Anil
Ooo good catch Anil - vous avez tout à fait raison. @ D-Nice, peut-être ajouter votre migration pour cette table à votre message au cas où il y aurait quelque chose d'étrange? Tentative de modifier cet user_id pour éliminer la confusion ..
MBHNYC
Non, désolé, ce n'était pas clair, l'identifiant de la société est ce qui est dupliqué. L'utilisateur existe déjà et est une constante dans ce cas. C'est en fait un problème hérité que je dois régler. Éditera le message avec plus d'informations
D-Nice
Vous n'avez pas besoin de recréer la table. Voir juste ma réponse pour réinitialiser la séquence.
Dondi Michael Stroma

Réponses:

87

Rails utilise probablement la séquence PostgreSQL intégrée. L'idée d'une séquence est qu'elle n'est utilisée qu'une seule fois.

La solution la plus simple consiste à définir la séquence de la colonne company.id sur la valeur la plus élevée de la table avec une requête comme celle-ci:

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

Je devine votre nom de séquence "company_id_seq", le nom de table "company" et le nom de colonne "id" ... veuillez les remplacer par les bons. Vous pouvez obtenir le nom de la séquence avec SELECT pg_get_serial_sequence('tablename', 'columname');ou consulter la définition de la table avec \d tablename.

Une autre solution consiste à remplacer la méthode save () dans votre classe d'entreprise pour définir manuellement l'ID de société pour les nouvelles lignes avant d'enregistrer.

Dondi Michael Stroma
la source
Je suppose que ce que cela ferait, c'est que l'attribution automatique commence par ce qui est actuellement la valeur la plus élevée + 1?
D-Nice
Je pense que c'est la meilleure réponse à ma question, cependant, pour des raisons indépendantes, je vais devoir trouver un moyen d'utiliser la stratégie que j'ai décrite dans mon édition OP
D-Nice
Je ne comprends pas pourquoi c'est arrivé au début? Cela m'est arrivé et j'aimerais comprendre comment cela est même possible.
Hunt Burdick
2
@Websitescenes, si l'on a une colonne SERIAL dans PostgreSQL (une colonne serial est une colonne dans laquelle la valeur par défaut est la valeur suivante dans une séquence), alors remplit la table avec des valeurs hard dans cette colonne, la séquence ne sera pas automatiquement mise à jour. Exemple: 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
206

J'ai fait cela, ce qui a résolu le problème pour moi.

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

J'ai trouvé le reset_pk_sequence! à partir de ce fil. http://www.ruby-forum.com/topic/64428

Une tarte
la source
4
Merci, la meilleure solution. Après le transfert de la base de données, j'ai eu le même problème.
Oleg Pasko
62
Ou l'équivalent one-liner (pour les rails de la console copier / coller):ActiveRecord::Base.connection.tables.each { |t| ActiveRecord::Base.connection.reset_pk_sequence!(t) }
Raf
Une idée de la façon dont cela se désynchronise?
Tall Paul
25

Basé sur la réponse @Apie .

Vous pouvez créer une tâche et l'exécuter lorsque vous en avez besoin avec:

rake database:correction_seq_id

Vous créez des tâches comme celle-ci:

rails g task database correction_seq_id

Et dans le fichier generated ( lib/tasks/database.rake) mettez:

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
inye
la source
4

Cela me semble être un problème de base de données et non un problème de Rails. Est-il possible que votre base de données ait une graine d'identité incorrecte sur votre idcolonne? Pour tester, essayez de faire quelques insertions directement dans votre base de données et voyez si le même comportement existe.

mynameiscoffey
la source
3
Pourquoi le vote négatif? C'est le comportement exact qui se produit si vous définissez votre séquence d'incrémentation sur quelque chose qui est inférieur aux autres valeurs existantes et que vous rencontrez donc parfois des collisions lors de l'insertion de données. L'affiche a déjà dit qu'il existe des données existantes qui relèvent de ce cas.
mynameiscoffey
Je peux bien insérer. Après avoir obtenu cette erreur, je peux exécuter à nouveau la même action et la faire fonctionner, si le prochain identifiant de la séquence n'est pas encore pris.
D-Nice
Il semble que c'était ma situation - j'ai rencontré le problème, mais le prochain enregistrement que j'ai inséré a bien fonctionné, il a donc dû mettre la graine au bon endroit.
Ben Wheeler
3

J'ai résolu ce problème en suivant la commande.

Exécutez ceci dans votre console de rails

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