ค้นหาแถวที่มีช่องที่ซ้ำกันหลายช่องด้วย 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-speak
Craig Ringer

คำตอบ:


225

เวอร์ชันทดสอบและใช้งานได้

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

นอกจากนี้สิ่งนี้ไม่เกี่ยวข้องเล็กน้อย แต่มีประโยชน์ หากต้องการดูจำนวนครั้งที่พบแต่ละชุดให้ใส่. ขนาดไว้ท้าย:

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
ฉันต้องส่งอาร์เรย์ Explict ไปที่select()as in: User.select([:first,:email]).group(:first,:email).having("count(*) > 1").countเพื่อให้ทำงานได้
Rafael Oliveira

4
เพิ่มการ.countให้PG::UndefinedFunction: ERROR: function count
Magne

1
คุณสามารถลองใช้ User.select ([: first,: email]). group (: first,: email) .having ("count (*)> 1"). map.count
Serhii Nadolynskyi

3
ฉันกำลังลองวิธีเดียวกัน แต่พยายามรับ User.id เช่นกันการเพิ่มลงใน select และกลุ่มจะส่งกลับอาร์เรย์ว่างเปล่า ฉันจะส่งคืนรูปแบบผู้ใช้ทั้งหมดหรืออย่างน้อยรวม: id ได้อย่างไร
Ashbury

6
ใช้.sizeแทน.count
Charles Hamel

33

ข้อผิดพลาดนั้นเกิดขึ้นเนื่องจาก POSTGRES ต้องการให้คุณใส่คอลัมน์การจัดกลุ่มในส่วนคำสั่ง SELECT

ลอง:

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

(หมายเหตุ: ไม่ได้ทดสอบคุณอาจต้องปรับแต่ง)

แก้ไขเพื่อลบคอลัมน์ id


7
มันจะไม่ทำงาน idคอลัมน์ไม่ได้เป็นส่วนหนึ่งของกลุ่มดังนั้นคุณจึงไม่สามารถอ้างได้เว้นแต่คุณจะรวม (เช่นarray_agg(id)หรือjson_agg(id))
เครก Ringer

10

หากคุณต้องการรุ่นเต็มให้ลองทำสิ่งต่อไปนี้ (ตามคำตอบของ @ newUserName นี่คือ)

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

สิ่งนี้จะส่งคืนแถวที่ที่อยู่อีเมลของแถวนั้นไม่ซ้ำกัน

ฉันไม่ทราบวิธีดำเนินการกับแอตทริบิวต์หลายรายการ


`` User.where (อีเมล: 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

ขอบคุณสำหรับวิธีแก้ปัญหาอย่างรวดเร็ว
RanaAlie

3

รับรายการที่ซ้ำกันทั้งหมดด้วยแบบสอบถามเดียวหากคุณใช้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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.