2010-01-19 10 views
36

He encontrado las respuestas "puro SQL" a esta pregunta. ¿Hay alguna manera, en Rails, para restablecer el campo id para una tabla específica?
¿Por qué quiero hacer esto? Porque tengo tablas con datos en constante movimiento, raramente más de 100 filas, pero siempre diferentes. Es hasta 25k ahora, y simplemente no tiene sentido. Tengo la intención de utilizar un planificador interno a la aplicación Rails (rufus-scheduler) para ejecutar el reinicio del campo de identificación mensualmente.Rieles forma de restablecer la semilla en el campo id

+2

Odio cuando la gente dice esto, pero los comentarios están bien ... realmente no hay manera de hacerlo esto en rieles, y, en general, no es necesario hacerlo en absoluto. Si solo está evitando obtener números grandes porque desea mostrarlos para los usuarios, solo debe tener una columna PID y usarla para su visualización. – aronchick

+0

¿Y cómo podría crear una columna PID? ¿Es eso lo mismo que Jonas sugirió a continuación en su respuesta? – Trevoke

Respuesta

51

Salí con una solución basada en la respuesta de hgimenez y this other one.

Como normalmente trabajo con Sqlite o PostgreSQL, solo he desarrollado para aquellos; pero extenderlo a, digamos MySQL, no debería ser demasiado problemático.

poner esto en el interior lib/y requieren en un inicializador:

# 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 

Uso:

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

EDIT: Ya que el caso más habitual en el que tendrá que hacer esto es después de limpiar una tabla de base de datos, recomiendo dar un vistazo a database_cleaner. Maneja el restablecimiento de ID automáticamente. Se puede decir que al eliminar tablas acaba de seleccionar como esto:

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

Si eres nuevo en Rails like me, [esta publicación del blog] (http://blog.chrisblunt.com/rails-3-how-to-autoload-and-autorequire-your-custom -library-code /) me ayudó a configurar este código en un inicializador. –

1

Un problema es que este tipo de campos se implementan de forma diferente para diferentes secuencias databases-, auto-incrementos, etc.

Siempre se puede quitar y volver a agregar la tabla.

1

No existe tal cosa en Rails. Si necesita una buena identificación para mostrar a los usuarios, guárdela en una tabla separada y vuelva a utilizarla.

+3

¡Eso es inteligente! Sin embargo, no se lo estoy mostrando al usuario. Solo quiero evitar desbordamientos en 10,000 años. :-) – Trevoke

1

Sólo se podía hacer esto en los carriles si los _ids se están estableciendo por raíles. Siempre que su base de datos establezca los _ids, no podrá controlarlos sin utilizar SQL.

Nota al margen: supongo que utilizar rails para llamar regularmente a un procedimiento de SQL que se restablece o descarta y recrea una secuencia no sería una solución puramente SQL, pero no creo que eso sea lo que estás preguntando ...

EDIT:

de responsabilidad: no sé mucho sobre rieles.

Desde la perspectiva de SQL, si usted tiene una tabla con columnas id first_name last_name y por lo general insert into table (first_name, last_name) values ('bob', 'smith') que sólo puede cambiar sus consultas a insert into table (id, first_name, last_name) values ([variable set by rails], 'bob', 'smith') De esta manera, _ID está fijado por una variable, en lugar de ser ajustados automáticamente por SQL. En ese punto, los rieles tienen control total sobre lo que son los _ids (aunque si es un PK, necesitas asegurarte de no utilizar el mismo valor mientras todavía está allí).

Si va a salir de la asignación hasta la base de datos, usted tiene que tener carriles administrados (en cualquier tiempo del horario) algo como:

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

a cualquier secuencia controla los identificadores para su mesa. Esto eliminará la secuencia actual y creará una nueva. Esta es la forma más sencilla que conozco de 'restablecer' una secuencia.

+0

Bueno, tendré que usar Rails para llamar regularmente a una instrucción SQL para volver a inicializar, creo :) Por curiosidad, ¿cómo haría que Rails establezca el _id? – Trevoke

114

Nunca mencionó qué DBMS está utilizando. Si esto es PostgreSQL, el postgres adaptador ActiveRecord tiene un método reset_pk_sequences! que se puede utilizar:

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

Gracias, Harold. Esto me está salvando el culo ahora mismo. –

+0

¡Buenas cosas, Harold! – RubyFanatic

+11

El nombre de la tabla puede ser inflexionado por Rails con 'Class.table_name', que puede hacerlo aún más agradable. ¡Gracias! –

5

Asumo que no se preocupan por los datos:

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

O si lo hace, pero no demasiado (hay una porción de tiempo en que los datos sólo existe en la 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 

Esto dará a los nuevos registros, con los mismos atributos, sino de identificación a partir de 1.

Cualquier cosa belongs_to en esta tabla podría resultar molesto.

+0

Esa es una respuesta clara, pero truncar no es una opción para mí. Aunque aprendí algo :) – Trevoke

1

Basado en @hgmnz 's respuesta, he hecho este método que va a determinar la secuencia en cualquier valor que te gusta ... (Sólo probado con los Postgres adaptador)

# 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

Esto funciona fuera de la caja ... ¡gracias! – ChaosFreak

0

Rieles, por ejemplo, para MySQL, pero con perdido todos los datos de la tabla users:

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

Tal vez ayude a alguien;)

+0

Esta es la misma respuesta que https://stackoverflow.com/a/2098639/234025. – Trevoke

Cuestiones relacionadas