Active Record, Rails 및 Postgres로 중복 필드가 여러 개인 행 찾기


103

Postgres 및 Activerecord를 사용하여 여러 열에서 중복 값이있는 레코드를 찾는 가장 좋은 방법은 무엇입니까?

이 솔루션을 여기 에서 찾았 습니다 .

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

그러나 그것은 postgres에서 작동하지 않는 것 같습니다. 이 오류가 발생합니다.

PG :: GroupingError : ERROR : "parts.id"열이 GROUP BY 절에 나타나거나 집계 함수에 사용되어야합니다.


3
일반 SQL에서는 .NET과 같은 자체 조인을 사용합니다 select a.id, b.id, name, email FROM user a INNER JOIN user b USING (name, email) WHERE a.id > b.id. ActiveRecord-speak에서 그것을 표현하는 방법을 모릅니다.
Craig Ringer 2014

답변:


223

테스트 및 작동 버전

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()다음 User.select([:first,:email]).group(:first,:email).having("count(*) > 1").count과 같이 명시 적 배열을 전달 해야했습니다.
Rafael Oliveira 2014 년

4
이 추가 .count부여합니다PG::UndefinedFunction: ERROR: function count
마그네

1
당신은 User.select를 ([첫째 : 이메일]) 시도 할 수 있습니다 그룹 (첫째 : 이메일) .having ( "카운트 (*)> 1") map.count..
Serhii Nadolynskyi

3
동일한 방법을 시도하고 있지만 User.id도 가져 오려고 시도하여 선택 및 그룹에 추가하면 빈 배열이 반환됩니다. 전체 사용자 모델을 반환하거나 최소한 : id를 포함하려면 어떻게해야합니까?
Ashbury

5
사용 .size대신에.count
찰스 하멜

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

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

답변을 기반으로 @newUserNameHere 여기에 각각의 수를 표시하는 올바른 방법은

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

res.each {|r| puts r.attributes } ; nil
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.