2010-01-19 11 views
36

Ho trovato le risposte "SQL puro" a questa domanda. C'è un modo, in Rails, per reimpostare il campo id per una tabella specifica?
Perché voglio farlo? Perché ho tabelle con dati in costante movimento - raramente più di 100 righe, ma sempre diverse. È fino a 25k ora, e non ha proprio senso. Intendo utilizzare uno scheduler interno all'app Rails (rufus-scheduler) per eseguire il reset del campo ID mensilmente o giù di lì.Rails per ripristinare i semi sul campo ID

+2

Odio quando la gente dice questo, ma i commenti sono di destra ... non c'è davvero nessun modo per farlo questo in binari, e, in generale, non è necessario farlo affatto. Se stai solo evitando di ottenere numeri grandi perché vuoi visualizzarli per gli utenti, dovresti semplicemente avere una colonna PID e usarla per il tuo display. – aronchick

+0

E come andrei sulla creazione di una colonna PID? È la stessa cosa suggerita da Jonas nella sua risposta? – Trevoke

risposta

51

Sono uscito con una soluzione basata sulla risposta di hgimenez e this other one.

Poiché di solito lavoro con Sqlite o PostgreSQL, ho sviluppato solo per quelli; ma estendendolo a, diciamo MySQL, non dovrebbe essere troppo fastidioso.

mettere questo dentro lib/e richiedono su un inizializzatore:

# lib/active_record/add_reset_pk_sequence_to_base.rb 
module ActiveRecord 
    class Base 
    def self.reset_pk_sequence 
     case ActiveRecord::Base.connection.adapter_name 
     when 'SQLite' 
     new_max = maximum(primary_key) || 0 
     update_seq_sql = "update sqlite_sequence set seq = #{new_max} where name = '#{table_name}';" 
     ActiveRecord::Base.connection.execute(update_seq_sql) 
     when 'PostgreSQL' 
     ActiveRecord::Base.connection.reset_pk_sequence!(table_name) 
     else 
     raise "Task not implemented for this DB adapter" 
     end 
    end  
    end 
end 

Usage:

Client.count # 10 
Client.destroy_all 
Client.reset_pk_sequence 
Client.create(:name => 'Peter') # this client will have id=1 

EDIT: Dal momento che il caso più comune in cui si vuole fare questo è dopo aver eliminato una tabella di database, consiglio di dare un'occhiata a database_cleaner. Gestisce automaticamente il reset dell'ID. Si può dire che per eliminare le tabelle appena selezionate in questo modo:

DatabaseCleaner.clean_with(:truncation, :only => %w[clients employees]) 
+2

Se non conosci Rails come me, [questo post del blog] (http://blog.chrisblunt.com/rails-3-how-to-autoload-and-autorequire-your-custom -library-code /) mi ha aiutato a impostare questo codice in un inizializzatore. –

1

Un problema è che questi tipi di campi vengono implementati in modo diverso per diverse sequenze databases-, auto-incrementi, ecc

È sempre possibile eliminare e aggiungere nuovamente la tabella.

1

No, non c'è niente di simile in Rails. Se hai bisogno di un bel id per mostrare gli utenti, quindi li memorizza in una tabella separata e riutilizzarli.

+3

È intelligente! Non lo sto mostrando all'utente però. Voglio solo evitare di traboccare in 10.000 anni. :-) – Trevoke

1

Si poteva fare solo in Rails se i _ids vengono impostati dai binari. Finché i _id vengono impostati dal tuo database, non sarai in grado di controllarli senza usare SQL.

Nota a margine: Credo che usando le rotaie per chiamare regolarmente una procedura SQL che azzera o scende e ricrea una sequenza non sarebbe una soluzione puramente SQL, ma non credo che è quello che si sta chiedendo ...

EDIT:

Disclaimer: non so molto su rotaie.

Dal punto di vista SQL, se si dispone di una tabella con colonne id first_name last_name e di solito insert into table (first_name, last_name) values ('bob', 'smith') si può semplicemente cambiare le vostre domande a insert into table (id, first_name, last_name) values ([variable set by rails], 'bob', 'smith') In questo modo, il _id viene impostato da una variabile, invece di essere impostato automaticamente da SQL. A quel punto, le rotaie hanno il controllo completo su ciò che sono le _id (anche se si tratta di un PK è necessario assicurarsi di non utilizzare lo stesso valore mentre è ancora lì).

Se avete intenzione di lasciare l'incarico fino al database, è necessario disporre di rotaie Run (su qualunque tempo di programma) qualcosa come:

DROP SEQUENCE MY_SEQ; 
CREATE SEQUENCE MY_SEQ START WITH 1 INCREMENT BY 1 MINVALUE 1; 

a qualsiasi sequenza controlla gli ID per il vostro tavolo. Questo eliminerà la sequenza corrente e ne creerà una nuova. Questo è il modo più semplice che conosco per resettare una sequenza.

+0

Beh, dovrò usare Rails per chiamare regolarmente un'istruzione SQL per ri-seminare, penso :) Per curiosità, come faccio a impostare Rails per _id? – Trevoke

114

Non hai mai menzionato che DBMS stai usando. Se questo è PostgreSQL, l'adattatore Postgres ActiveRecord ha un metodo reset_pk_sequences! che si potrebbe usare:

ActiveRecord::Base.connection.reset_pk_sequence!('table_name') 
+0

Grazie, Harold. Questo mi sta salvando il culo adesso. –

+0

Roba buona Harold! – RubyFanatic

+11

Il nome della tabella può essere modificato da Rails con 'Class.table_name', che può renderlo ancora più bello. Grazie! –

5

Presumo che non si preoccupano i dati:

def self.truncate! 
    connection.execute("truncate table #{quoted_table_name}") 
end 

o se, ma non troppo (c'è una fetta di tempo in cui i dati esiste solo nella memoria):

def self.truncate_preserving_data! 
    data = all.map(&:clone).each{|r| raise "Record would not be able to be saved" unless r.valid? } 
    connection.execute("truncate table #{quoted_table_name}") 
    data.each(&:save) 
end 

Questo darà nuovi record, con gli stessi attributi, ma id a partire da 1.

Qualsiasi cosa belongs_to questo tavolo potrebbe diventare faticoso.

+0

Questa è una risposta chiara, ma il troncamento non è un'opzione per me. Ho imparato qualcosa :) – Trevoke

1

in base alla risposta @hgmnz s', ho fatto questo metodo che impostare la sequenza a qualsiasi valore che ti piace ... (Solo testato con i Postgres adattatore)

# change the database sequence to force the next record to have a given id 
def set_next_id table_name, next_id 
    connection = ActiveRecord::Base.connection 
    def connection.set_next_id table, next_id 
    pk, sequence = pk_and_sequence_for(table) 
    quoted_sequence = quote_table_name(sequence) 
    select_value <<-end_sql, 'SCHEMA' 
     SELECT setval('#{quoted_sequence}', #{next_id}, false) 
    end_sql 
    end 
    connection.set_next_id(table_name, next_id) 
end 
+0

Funziona fuori dalla scatola ... grazie! – ChaosFreak

0

Modo rotaie per es. MySQL, ma con persi tutti i dati in tabella users:

ActiveRecord::Base.connection.execute('TRUNCATE TABLE users;') 

aiuta Forse qualcuno;)

+0

Questa è la stessa risposta di https://stackoverflow.com/a/2098639/234025. – Trevoke