使用Active Record,Rails和Postgres查找具有多个重复字段的行


103

使用Postgres和Activerecord在多列中查找具有重复值的记录的最佳方法是什么?

我在这里找到了这个解决方案:

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

但这似乎不适用于postgres。我收到此错误:

PG :: GroupingError:错误:列“ parts.id”必须出现在GROUP BY子句中或在聚合函数中使用


3
在常规SQL中,我将使用自联接,例如select a.id, b.id, name, email FROM user a INNER JOIN user b USING (name, email) WHERE a.id > b.id。不知道如何用ActiveRecord来表达这一点。
克雷格·林格2014年

Answers:


221

测试和工作版本

User.select(:first,:email).group(:first,:email).having("count(*) > 1")

而且,这有点无关,但是很方便。如果要查看每个组合的查找时间,请在末尾添加.size:

User.select(:first,:email).group(:first,:email).having("count(*) > 1").size

然后您将得到如下所示的结果集:

{[nil, nil]=>512,
 ["Joe", "test@test.com"]=>23,
 ["Jim", "email2@gmail.com"]=>36,
 ["John", "email3@gmail.com"]=>21}

以为那很酷,以前没看过。

感谢Taryn,这只是她的答案的调整版本。


7
为了工作,我必须将显式数组传递给select()in:User.select([:first,:email]).group(:first,:email).having("count(*) > 1").count
拉斐尔·奥利维拉

4
加上.count给出PG::UndefinedFunction: ERROR: function count
马格纳

1
您可以尝试User.select([:first,:email])。group(:first,:email).having(“ count(*)> 1”)。map.count
Serhii Nadolynskyi

3
我正在尝试相同的方法,但也尝试获取User.id,将其添加到select和group会返回一个空数组。如何返回整个用户模型,或者至少包括:id?
阿什伯里2015年

5
使用.size代替.count
Charles Hamel

32

发生该错误是因为POSTGRES要求您将分组列放在SELECT子句中。

尝试:

User.select(:first,:email).group(:first,:email).having("count(*) > 1").all

(注意:未经测试,您可能需要对其进行调整)

编辑以删除ID列


7
那是行不通的。该id列不是组的一部分,所以除非你聚合它,你不能引用它(如array_agg(id)json_agg(id)
克雷格林格

9

如果需要完整的模型,请尝试以下操作(基于@newUserNameHere的回答)。

User.where(email: User.select(:email).group(:email).having("count(*) > 1").select(:email))

这将返回行的电子邮件地址不唯一的行。

我不知道在多个属性上执行此操作的方法。


```User.where(email:User.select(:email).group(:email).having(“ count(*)> 1”))```
chet corey

谢谢,我的作品很棒:)似乎最后一个.select(:email)也是多余的。我认为这会更清洁一点,但是我可能是错的。 User.where(email: User.select(:email).group(:email).having("count(*) > 1"))
chet corey 18/12/26

2

如果使用PostgreSQL,则通过一个查询获取所有重复项:

def duplicated_users
  duplicated_ids = User
    .group(:first, :email)
    .having("COUNT(*) > 1")
    .select('unnest((array_agg("id"))[2:])')

  User.where(id: duplicated_ids)
end

irb> duplicated_users

-1

根据上面 @newUserName 的答案,我认为显示每个计数的正确方法是

res = User.select('first, email, count(1)').group(:first,:email).having('count(1) > 1')

res.each {|r| puts r.attributes } ; nil
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.