2010-12-30 2 views
13

У меня есть пользователи, у которых есть поля first_name и last_name, и мне нужно сделать ruby, чтобы найти всех пользователей, у которых есть дубликаты учетных записей, основанные на именах и фамилиях. Например, я хочу найти находку, которая будет искать всех остальных пользователей и найти, имеют ли они одно и то же имя и адрес электронной почты. Я думал, вложенный цикл, как этотПоиск всех пользователей, у которых есть повторяющиеся имена

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 

Есть ли лучший способ

+0

Вы проверяете его перед сохранением нового пользователя или вам нужно найти дубликатов пользователей? – tommasop

+0

Мне нужно найти дубликатов пользователей – Trace

+0

У пользователей будут регулярно дублироваться имена пользователей.Если вы используете свои имена для предоставления уникальных учетных записей, вы без лишних ограничений ограничиваете свою пользовательскую базу. Вместо этого начните с адреса электронной почты, который будет уникальным в Интернете. –

ответ

33

Вы могли бы пойти длинный путь к сужению поиска, находя, что дублированный данные в первую очередь. Например, скажем, вы хотите найти каждую комбинацию имени и электронной почты, которые используются более одного раза.

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

Это вернет массив, содержащий одну из каждой дублированной записи. Из этого следует, что один из возвращаемых пользователей имел «Fred» и «[email protected]», тогда вы могли искать только тех пользователей, у которых есть эти значения, чтобы найти всех затронутых пользователей.

Возврат от find будет примерно следующим. Обратите внимание, что массив содержит только одну запись из каждого набора дублированных пользователей.

[#<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">] 

Например, первый элемент в этом массиве показывает один пользователь с «Foo» и «[email protected]». Остальные из них можно вытащить из базы данных по мере необходимости с помощью find.

> 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">] 

И это также кажется, что вы хотите, чтобы добавить некоторые лучше валидацию кода для предотвращения дубликатов в будущем.

Edit:

Если вам нужно использовать большой молоток find_by_sql, потому что Rails 2.2 и более ранние версии не поддерживают :having с find, должны работать и дать вам тот же массив, который я описал выше ,

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

Мне нравится ваш ответ, но есть только для рельсов 2.3 и выше, а приложение 2.2. – Trace

+0

Хм, странно. Клянусь, что я использую: в течение многих лет, но моя память может быть короткой. Вам, возможно, придется прибегать к использованию 'find_by_sql' и ручному составлению предложений группы /, но я тоже буду копать вас. http://api.rubyonrails.org/classes/ActiveRecord/Base.html#method-c-find_by_sql – jdl

+0

Это именно то, что мне нужно. Как бы вы это сделали в мангоиде? Вы не можете использовать группу в mongoid, и я не думаю, что вы можете использовать ее. Надеюсь, ты сможешь вести меня в правильном направлении. –

0

Через некоторое Googling, я закончил с этим:

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 

Обратите внимание, что часть, которая говорит IS NOT DISTINCT FROM, это, чтобы помочь справиться с NULL значениями, которые не могут быть сопоставлены с знаком равенства в Postgres.

+0

Что делать, если вы хотите вернуть идентификаторы дубликатов записей? Ответ в сообщении SO, на который вы ссылаетесь, просто возвращает ассоциации имени/электронной почты с обманами ... в идеале я бы возвратил массив массивов, каждый массив, содержащий идентификаторы одной дублирующей группы. –

+0

Да, я закончил использование пользовательского SQL. – hakunin

0

Если вы собираетесь маршрут @hakunin и создание запроса вручную, вы можете использовать следующее:

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 

Изменения заменяемого connection.execute(<<-SQL) с connection.exec_query(<<-SQL)

Там может быть проблема с утечкой памяти с использованием execute

Просьба прочитать Clarify DataBaseStatements#execute, чтобы получить глубокое понимание проблемы.

Смежные вопросы