2010-12-30 12 views
13

que tienen los usuarios de los cuales tiene campos first_name y last_name y tengo que hacer un rubí encontrar todos los usuarios que tienen cuentas duplicadas basado en nombres y apellidos. Por ejemplo quiero tener un hallazgo que buscar a través de todos los otros usuarios y encontrar si alguno tienen el mismo nombre y correo electrónico. Estaba pensando en un bucle anidado como estela búsqueda de todos los usuarios que tienen nombres duplicados

User.all.each do |user| 
//maybe another loop to search through all the users and maybe if a match occurs put that user in an array 
end 

¿Hay una mejor manera

+0

¿Está comprobando antes de guardar un nuevo usuario o que necesitan encontrar usuarios duplicados? – tommasop

+0

Necesito encontrar usuarios duplicados – Trace

+0

Los nombres de usuario van regularmente para tener duplicados.Si está usando sus nombres para proporcionar cuentas únicas, limitará innecesariamente su base de usuarios. En cambio, comience con su dirección de correo electrónico, que será única en Internet. –

Respuesta

33

Usted podría ir un largo camino hacia reducir su búsqueda por descubrir lo que los datos duplicada está en el primer lugar. Por ejemplo, supongamos que desea buscar cada combinación de nombre y correo electrónico que se usa más de una vez.

User.find(:all, :group => [:first, :email], :having => "count(*) > 1") 

Devolverá una matriz que contenga uno de cada uno de los registros duplicados. A partir de eso, decir que uno de los usuarios volvieron tenían "Fred" y "[email protected]" entonces se podría buscar sólo los usuarios que tienen esos valores para encontrar todos los usuarios afectados.

El regreso de find que será algo así como lo siguiente. Tenga en cuenta que la matriz solo contiene un solo registro de cada conjunto de usuarios duplicados.

[#<User id: 3, first: "foo", last: "barney", email: "[email protected]", created_at: "2010-12-30 17:14:43", updated_at: "2010-12-30 17:14:43">, 
#<User id: 5, first: "foo1", last: "baasdasdr", email: "[email protected]", created_at: "2010-12-30 17:20:49", updated_at: "2010-12-30 17:20:49">] 

Por ejemplo, el primer elemento en esa matriz muestra un usuario con "foo" y "[email protected]". El resto de ellos puede extraerse de la base de datos según sea necesario con un descubrimiento.

> User.find(:all, :conditions => {:email => "[email protected]", :first => "foo"}) 
=> [#<User id: 1, first: "foo", last: "bar", email: "[email protected]", created_at: "2010-12-30 17:14:28", updated_at: "2010-12-30 17:14:28">, 
    #<User id: 3, first: "foo", last: "barney", email: "[email protected]", created_at: "2010-12-30 17:14:43", updated_at: "2010-12-30 17:14:43">] 

Y También parece que se le quiere añadir un poco mejor la validación de su código para evitar duplicados en el futuro.

Editar:

Si necesita utilizar el martillo grande de find_by_sql, porque Rails 2.2 y anteriores no apoyaron :having con find, el siguiente debe funcionar y le dará la misma matriz que he descrito anteriormente .

User.find_by_sql("select * from users group by first,email having count(*) > 1") 
+2

me gusta tu respuesta, pero es sólo para tener carriles de 2.3 y anteriores y la aplicación es de 2,2 – Trace

+0

Hmm, extraña. Juro que he estado usando: tener durante años, pero mi memoria puede haber cortocircuitado. Puede que tenga que recurrir al uso de 'find_by_sql' y de creación manual del grupo/tener cláusulas, pero voy a cavar un poco para usted también. http://api.rubyonrails.org/classes/ActiveRecord/Base.html#method-c-find_by_sql – jdl

+0

Esto es justo lo que necesito. ¿Cómo harías esto en Mongoid? No puedes usar group en mongoid y no creo que puedas usar having. Espero que puedas guiarme en la dirección correcta. –

0

Después de algunas google, terminé con esto:

ActiveRecord::Base.connection.execute(<<-SQL).to_a 
    SELECT 
    variants.id, variants.variant_no, variants.state 
    FROM variants INNER JOIN (
    SELECT 
     variant_no, state, COUNT(1) AS count 
    FROM variants 
    GROUP BY 
     variant_no, state HAVING COUNT(1) > 1 
) tt ON 
    variants.variant_no = tt.variant_no 
    AND variants.state IS NOT DISTINCT FROM tt.state; 
SQL 

Tenga en cuenta que parte que dice IS NOT DISTINCT FROM, esto es para ayudar a lidiar con NULL valores, que no se pueden comparar con signo igual en postgres

+0

¿Qué sucede si desea devolver los identificadores de los registros duplicados? La respuesta en el pliego de publicar se vincula a sólo devuelve los cominations de nombre/correo electrónico que tienen engañados ... Lo ideal sería que me gustaría volver una matriz de matrices, cada una matriz que contiene los identificadores de un grupo duplicado. –

+0

Sí, terminé usando SQL personalizado. – hakunin

0

Si usted va la ruta de @hakunin y la creación de una consulta de forma manual, es posible que desee utilizar la siguiente:

ActiveRecord::Base.connection.exec_quey(<<-SQL).to_a 
    SELECT 
    variants.id, variants.variant_no, variants.state 
    FROM variants INNER JOIN (
    SELECT 
     variant_no, state, COUNT(1) AS count 
    FROM variants 
    GROUP BY 
     variant_no, state HAVING COUNT(1) > 1 
) tt ON 
    variants.variant_no = tt.variant_no 
    AND variants.state IS NOT DISTINCT FROM tt.state; 
SQL 

El cambio está reemplazando connection.execute(<<-SQL) con connection.exec_query(<<-SQL)

No puede haber una problema con la fuga de memoria usando execute

Pida leer Clarify DataBaseStatements#execute para obtener una comprensión profunda del problema.

Cuestiones relacionadas