ID de atribuição automática do Rails que já existe

92

Eu crio um novo registro assim:

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

Meu banco de dados atualmente tem vários milhares de entidades para caminhão, mas atribuí os ids a várias delas, de forma que deixei alguns ids disponíveis. Então o que está acontecendo é que o rails cria um item com id = 150 e funciona bem. Mas então ele tenta criar um item e atribuir a ele id = 151, mas esse id pode já existir, então estou vendo este erro:

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

E da próxima vez que eu executar a ação, ela simplesmente atribuirá o id 152, que funcionará bem se esse valor ainda não tiver sido usado. Como faço para que os trilhos verifiquem se um ID já existe antes de atribuí-lo?

Obrigado!

EDITAR

O ID do caminhão é o que está sendo duplicado. O usuário já existe e é uma constante neste caso. Na verdade, é um problema de legado com o qual tenho que lidar. Uma opção é recriar a tabela em let rails auto designar cada id desta vez. Estou começando a achar que essa pode ser a melhor escolha porque estou tendo alguns outros problemas, mas a migração para fazer isso seria muito complicada porque Truck é uma chave estrangeira em muitas outras tabelas. Haveria uma maneira simples de os trilhos criarem uma nova tabela com os mesmos dados já armazenados em Truck, com IDs atribuídos automaticamente e mantendo todos os relacionamentos existentes?

D-Nice
fonte
por que você não está permitindo que os rails atribuam o ID automaticamente? Isso eliminaria qualquer perigo de duplicação - ou esse é um problema de dados legados em que você deve manter IDs antigos? Só quero entender um pouco o caso de negócios, uma vez que isso não é normal ao criar um novo objeto.
MBHNYC
@MBHNYC Acho que D-Nice está atribuindo um user_id ao criar a empresa, e não id como você está pensando (e eu também fiz por um momento).
Anil
Ooo boa captura Anil - você está totalmente certo. @ D-Legal, talvez adicione sua migração para esta tabela ao seu post, caso haja algo estranho? É tentador editar esse user_id para eliminar a confusão.
MBHNYC
Não, desculpe, não estava claro, o ID da empresa é o que está sendo duplicado. O usuário já existe e é uma constante neste caso. Na verdade, é um problema de legado com o qual tenho que lidar. Editará a postagem com mais informações
D-Nice
Você não precisa recriar a tabela. Basta ver minha resposta para reiniciar a sequência.
Dondi Michael Stroma

Respostas:

87

Rails provavelmente está usando a sequência embutida do PostgreSQL. A ideia de uma sequência é que ela seja usada apenas uma vez.

A solução mais simples é definir a sequência da coluna company.id para o valor mais alto da tabela com uma consulta como esta:

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

Estou supondo que seu nome de sequência "id_empresa_seq", nome da tabela "empresa" e nome da coluna "id" ... substitua-os pelos corretos. Você pode obter o nome da sequência com SELECT pg_get_serial_sequence('tablename', 'columname');ou examinar a definição da tabela com \d tablename.

Uma solução alternativa é substituir o método save () em sua classe de empresa para definir manualmente o id da empresa para novas linhas antes de salvar.

Dondi Michael Stroma
fonte
Eu estou supondo o que isso faria é iniciar a atribuição automática com o que é atualmente o valor mais alto + 1?
D-Nice
Acho que esta é a melhor resposta à minha pergunta, no entanto, por razões não relacionadas, terei que encontrar uma maneira de usar a estratégia que descrevi na minha edição de OP
D-Nice
Eu não entendo por que isso aconteceu para começar? Isso já aconteceu comigo e gostaria de entender como isso é possível.
Hunt Burdick
2
@Websitescenes, se houver uma coluna SERIAL no PostgreSQL (uma coluna serial é aquela em que o valor padrão é o próximo valor em uma sequência), então preenche a tabela com valores fixos nessa coluna, a sequência não será atualizada automaticamente. Exemplo: 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
204

Fiz isso que resolveu o problema para mim.

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

Encontrei o reset_pk_sequence! deste tópico. http://www.ruby-forum.com/topic/64428

Uma torta
fonte
4
Obrigado, a melhor solução. Após a transferência do banco de dados, tive o mesmo problema.
Oleg Pasko,
61
Ou o equivalente de uma linha (para fins de copiar / colar do console do Rails):ActiveRecord::Base.connection.tables.each { |t| ActiveRecord::Base.connection.reset_pk_sequence!(t) }
Raf
Alguma ideia de como isso fica fora de sincronia?
Tall Paul
25

Com base na resposta @Apie .

Você pode fazer uma tarefa e executá-la quando precisar com:

rake database:correction_seq_id

Você cria tarefas como esta:

rails g task database correction_seq_id

E no arquivo gerado ( lib/tasks/database.rake) coloque:

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
fonte
4

Parece-me um problema de banco de dados e não um problema de Rails. É possível que seu banco de dados tenha uma semente de identidade imprópria em sua idcoluna? Para testar, tente fazer algumas inserções diretamente em seu banco de dados e veja se o mesmo comportamento existe.

mynameiscoffey
fonte
3
Por que o downvote? Este é o comportamento exato que acontece se você definir sua sequência de incremento para algo que seja menor do que outros valores existentes e, portanto, ocasionalmente atinge colisões ao inserir dados. O autor já disse que existem dados que se enquadram neste caso.
mynameiscoffey
Eu posso inserir bem. Depois de receber esse erro, posso realmente executar a mesma ação novamente e fazê-la funcionar, se o próximo id na sequência ainda não tiver sido executado.
D-Nice
parece que essa era a minha situação - encontrei o problema, mas o próximo registro que inseri funcionou bem, então deve ter colocado a semente no lugar certo.
Ben Wheeler de
3

Resolvi esse problema seguindo o comando.

Execute isso em seu console de trilhos

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