ruby-on-rails-3 - repetidos - quitar duplicados excel con condicion
¿Eliminar registros duplicados basados en múltiples columnas? (6)
Para ejecutarlo en una migración, terminé haciendo lo siguiente (basado en la respuesta anterior de @ aditya-sanghi)
class AddUniqueIndexToXYZ < ActiveRecord::Migration
def change
# delete duplicates
dedupe(XYZ, ''name'', ''type'')
add_index :xyz, [:name, :type], unique: true
end
def dedupe(model, *key_attrs)
model.select(key_attrs).group(key_attrs).having(''count(*) > 1'').each { |duplicates|
dup_rows = model.where(duplicates.attributes.slice(key_attrs)).to_a
# the first one we want to keep right?
dup_rows.shift
dup_rows.each{ |double| double.destroy } # duplicates can now be destroyed
}
end
end
Estoy usando Heroku para alojar mi aplicación Ruby on Rails y, por una razón u otra, es posible que tenga algunas filas duplicadas.
¿Hay alguna manera de eliminar registros duplicados en base a 2 o más criterios pero mantener solo 1 registro de esa colección duplicada?
En mi caso de uso, tengo una relación de Marca y Modelo para automóviles en mi base de datos.
Make Model
--- ---
Name Name
Year
Trim
MakeId
Me gustaría eliminar todos los registros del modelo que tienen el mismo nombre, año y recorte, pero guardo 1 de esos registros (es decir, necesito el registro, pero solo una vez). Estoy usando la consola Heroku para poder ejecutar algunas consultas de registro activas fácilmente.
¿Alguna sugerencia?
Puede intentar lo siguiente: (basado en respuestas anteriores)
ids = Model.group(''name, year, trim'').pluck(''MIN(id)'')
para obtener todos los registros válidos Y entonces:
Model.where.not(id: ids).destroy_all
para eliminar los registros innecesarios. Y ciertamente, puede hacer una migración que agregue un índice único para las tres columnas, de modo que esto se aplique en el nivel de la base de datos:
add_index :models, [:name, :year, :trim], unique: true
Puede probar esta consulta SQL, para eliminar todos los registros duplicados, pero el último
DELETE FROM users USING users user WHERE (users.name = user.name AND users.year = user.year AND users.trim = user.trim AND users.id < user.id);
Si los datos de su tabla de usuario son los siguientes
User.all =>
[
#<User id: 15, name: "a", email: "[email protected]", created_at: "2013-08-06 08:57:09", updated_at: "2013-08-06 08:57:09">,
#<User id: 16, name: "a1", email: "[email protected]", created_at: "2013-08-06 08:57:20", updated_at: "2013-08-06 08:57:20">,
#<User id: 17, name: "b", email: "[email protected]", created_at: "2013-08-06 08:57:28", updated_at: "2013-08-06 08:57:28">,
#<User id: 18, name: "b1", email: "[email protected]", created_at: "2013-08-06 08:57:35", updated_at: "2013-08-06 08:57:35">,
#<User id: 19, name: "b11", email: "[email protected]", created_at: "2013-08-06 09:01:30", updated_at: "2013-08-06 09:01:30">,
#<User id: 20, name: "b11", email: "[email protected]", created_at: "2013-08-06 09:07:58", updated_at: "2013-08-06 09:07:58">]
1.9.2p290 :099 >
Los ID de correo electrónico están duplicados, por lo que nuestro objetivo es eliminar todos los ID de correo electrónico duplicados de la tabla de usuario.
Paso 1:
Para obtener todos los registros de correo electrónico diferentes.
ids = User.select("MIN(id) as id").group(:email,:name).collect(&:id)
=> [15, 16, 18, 19, 17]
Paso 2:
Para eliminar identificaciones duplicadas de la tabla de usuario con distintas identificaciones de correo electrónico.
Ahora la matriz ids contiene los siguientes identificadores.
[15, 16, 18, 19, 17]
User.where("id NOT IN (?)",ids) # To get all duplicate records
User.where("id NOT IN (?)",ids).destroy_all
** RAILS 4 **
ActiveRecord 4 presenta el método .not
que le permite escribir lo siguiente en el Paso 2:
User.where.not(id: ids).destroy_all
Similar a la respuesta de @Aditya Sanghi, pero de esta manera será más eficaz porque solo está seleccionando los duplicados, en lugar de cargar todos los objetos del Modelo en la memoria y luego iterar sobre todos ellos.
# returns only duplicates in the form of [[name1, year1, trim1], [name2, year2, trim2],...]
duplicate_row_values = Model.select(''name, year, trim, count(*)'').group(''name, year, trim'').having(''count(*) > 1'').pluck(:name, :year, :trim)
# load the duplicates and order however you wantm and then destroy all but one
duplicate_row_values.each do |name, year, trim|
Model.where(name: name, year: year, trim: trim).order(id: :desc)[1..-1].map(&:destroy)
end
Además, si realmente no desea datos duplicados en esta tabla, probablemente desee agregar un índice único de varias columnas a la tabla, algo como:
add_index :models, [:name, :year, :trim], unique: true, name: ''index_unique_models''
class Model
def self.dedupe
# find all models and group them on keys which should be common
grouped = all.group_by{|model| [model.name,model.year,model.trim,model.make_id] }
grouped.values.each do |duplicates|
# the first one we want to keep right?
first_one = duplicates.shift # or pop for last one
# if there are any more left, they are duplicates
# so delete all of them
duplicates.each{|double| double.destroy} # duplicates can now be destroyed
end
end
end
Model.dedupe
- Encuentra todos
- Agrúpelos en las claves que necesita para la singularidad
- Bucle en los valores del modelo agrupado del hash
- elimine el primer valor porque quiere retener una copia
- elimina el resto