Active Record, Rails & Postgres ile birden çok yinelenen alana sahip satırları bulun


103

Postgres ve Activerecord kullanarak birden çok sütunda yinelenen değerlere sahip kayıtları bulmanın en iyi yolu nedir?

Bu çözümü burada buldum :

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

Ancak postgres ile çalışmıyor gibi görünüyor. Bu hatayı alıyorum:

PG :: GroupingError: ERROR: "parts.id" sütunu GROUP BY yan tümcesinde görünmeli veya bir toplama işlevinde kullanılmalıdır


3
Normal SQL'de kendi kendine katılma gibi bir şey kullanırdım select a.id, b.id, name, email FROM user a INNER JOIN user b USING (name, email) WHERE a.id > b.id. ActiveRecord konuşmasında bunu nasıl ifade edeceğimi bilmiyorum.
Craig Ringer

Yanıtlar:


225

Test Edilmiş ve Çalışma Sürümü

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

Ayrıca, bu biraz ilgisiz ama kullanışlıdır. Her kombinasyonun nasıl bulunduğunu görmek istiyorsanız sonuna .size yazın:

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

ve şuna benzeyen bir sonuç grubu alacaksınız:

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

Bunun oldukça havalı olduğunu ve daha önce görmediğini düşündüm.

Taryn'e kredi verin, bu onun cevabının sadece ince ayarlanmış bir versiyonu.


7
Çalışmak için bir explict dizisini select()in: olarak geçirmem User.select([:first,:email]).group(:first,:email).having("count(*) > 1").countgerekiyordu.
Rafael Oliveira

4
.countverir eklemePG::UndefinedFunction: ERROR: function count
Magne

1
User.select ([: first,: email]). Group (: first,: email) .having ("count (*)> 1"). Map.count
Serhii Nadolynskyi

3
Aynı yöntemi deniyorum ama User.id'yi de almaya çalışıyorum, onu select ve group'a eklemek boş bir dizi döndürüyor. Tüm Kullanıcı modelini nasıl iade edebilirim veya en azından: id'yi nasıl ekleyebilirim?
Ashbury

6
.sizeyerine kullanın.count
Charles Hamel

33

POSTGRES, SELECT yan tümcesine gruplandırma sütunları koymanızı gerektirdiğinden bu hata oluşur.

Deneyin:

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

(not: test edilmedi, ince ayar yapmanız gerekebilir)

İd sütununu kaldırmak için DÜZENLENDİ


7
Bu işe yaramayacak; idSütun bunu araya sürece bunu ifade edemez, böylece grubun parçası değildir (örn array_agg(id)ya json_agg(id))
Craig Zil

10

Tam modellere ihtiyacınız varsa, aşağıdakileri deneyin (@ newUserNameHere'ın cevabına göre).

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

Bu, satırın e-posta adresinin benzersiz olmadığı satırları döndürür.

Bunu birden çok öznitelik üzerinden yapmanın bir yolunu bilmiyorum.


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

Teşekkürler harika çalışıyor :) Ayrıca sonuncusu .select(:email)gereksiz gibi görünüyor . Sanırım bu biraz daha temiz, ama yanılıyor da olabilirim. User.where(email: User.select(:email).group(:email).having("count(*) > 1"))
chet corey

Hızlı çözüm için teşekkürler.
RanaAlie

3

PostgreSQL kullanıyorsanız, tüm kopyaları tek bir sorgu ile alın :

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

Yukarıdaki @ newUserName tarafından verilen yanıta dayanarak, her birinin sayısını göstermenin doğru yolunun

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

res.each {|r| puts r.attributes } ; nil
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.