Rails: เลือกค่าที่ไม่ซ้ำจากคอลัมน์


238

ฉันมีวิธีแก้ปัญหาการทำงานอยู่แล้ว แต่ฉันอยากรู้ว่าทำไมสิ่งนี้ถึงใช้งานไม่ได้:

ratings = Model.select(:rating).uniq
ratings.each { |r| puts r.rating }

มันเลือก แต่ไม่พิมพ์ค่าที่ไม่ซ้ำกันมันจะพิมพ์ค่าทั้งหมดรวมถึงรายการที่ซ้ำกัน และอยู่ในเอกสารประกอบ: http://guides.rubyonrails.org/active_record_querying.html#selecting-specific-fields


2
อีกตัวอย่างหนึ่งที่มี uniq stackoverflow.com/questions/8369812/…
manish nautiyal

คำตอบ:


449
Model.select(:rating)

ผลลัพธ์ของสิ่งนี้คือชุดของModelวัตถุ ไม่ใช่เรตติ้งธรรมดา และจากuniqมุมมองพวกเขาต่างกันโดยสิ้นเชิง คุณสามารถใช้สิ่งนี้:

Model.select(:rating).map(&:rating).uniq

หรือสิ่งนี้ (มีประสิทธิภาพมากที่สุด)

Model.uniq.pluck(:rating)

# rails 5+
Model.distinct.pluck(:rating)

ปรับปรุง

เห็นได้ชัดว่าราว 5.0.0.1 ทำงานได้กับข้อความค้นหา "ระดับสูงสุด" เท่านั้นเช่นด้านบน ไม่ทำงานบนพร็อกซีคอลเลกชัน (ตัวอย่างเช่นความสัมพันธ์ "has_many")

Address.distinct.pluck(:city) # => ['Moscow']
user.addresses.distinct.pluck(:city) # => ['Moscow', 'Moscow', 'Moscow']

ในกรณีนี้ให้ขจัดข้อมูลซ้ำซ้อนหลังจากแบบสอบถาม

user.addresses.pluck(:city).uniq # => ['Moscow']

ฉันทำ: กลุ่ม (: การให้คะแนน) .collect {| r | r.rating} เนื่องจาก map == รวบรวมฉันจะอ่านเกี่ยวกับซินแทกซ์ที่คุณใช้ (&: ให้คะแนน) ได้ที่ไหน ฉันไม่เห็นสิ่งนี้ในเอกสารของ Ruby
alexandrecosta

@ user1261084: ดูSymbol # to_procเพื่อทำความเข้าใจ. map (&: rating) PragDave อธิบาย
dbenhur

63
มันน่าสังเกตว่า Model.uniq.pluck(:rating)เป็นวิธีที่มีประสิทธิภาพมากที่สุดในการทำเช่นนี้ - สร้าง SQL ซึ่งใช้SELECT DISTINCTแทนการใช้.uniqกับอาร์เรย์
Mikey

23
ใน Rails 5 Model.uniq.pluck(:rating)จะเป็นModel.distinct.pluck(:rating)
neurodynamic

2
ถ้าคุณต้องการเลือกค่าที่ไม่ซ้ำจาก has_many คุณสามารถทำได้Model.related_records.group(:some_column).pluck(:some_column)
Krzysztof Karski

92

หากคุณจะใช้Model.selectคุณก็อาจใช้เช่นกันDISTINCTเพราะมันจะคืนค่าเฉพาะ สิ่งนี้ดีกว่าเพราะมันหมายความว่ามันจะส่งคืนแถวที่น้อยลงและควรจะเร็วกว่าการส่งคืนจำนวนแถวแล้วบอกให้ Rails เลือกค่าที่ไม่ซ้ำกัน

Model.select('DISTINCT rating')

แน่นอนว่าสิ่งนี้จะให้ฐานข้อมูลของคุณเข้าใจDISTINCTคำหลักและสิ่งที่ควรทำ


6
Model.select("DISTINCT rating").map(&:rating)เพื่อให้ได้รับเรตติ้งที่หลากหลาย
กริช

ยอดเยี่ยมสำหรับผู้ที่ใช้แอพรุ่นเก่าโดยใช้ Rails 2.3
Mikey

3
ใช่ .. มันใช้งานได้ยอดเยี่ยม - แต่จะคืนค่าแอตทริบิวต์ DISTINCT เท่านั้น คุณจะคืนค่าโมเดลวัตถุทั้งหมดได้อย่างไรตราบใดที่มันแตกต่างกัน? เพื่อให้คุณสามารถเข้าถึงคุณลักษณะทั้งหมดในรูปแบบในกรณีที่แอตทริบิวต์นั้นไม่ซ้ำกัน
zero_cool

@Jackson_Sandland หากคุณต้องการโมเดลวัตถุนั่นจะต้องมีการสร้างอินสแตนซ์จากบันทึกในตาราง แต่คุณไม่ได้เลือกบันทึกเพียงค่าที่ไม่ซ้ำกัน (จากสิ่งที่อาจเป็นหลายระเบียน)
Benissimo

69

มันก็ใช้ได้เช่นกัน

Model.pluck("DISTINCT rating")

ฉันเชื่อว่าถอนได้คือ Ruby 1.9.x ขึ้นไป ทุกคนที่ใช้รุ่นก่อนหน้าจะไม่มีมัน ถ้าคุณอยู่ใน 1.9x ขึ้นไปเอกสารทับทิมบอกว่ามันใช้งานได้เช่นกัน: Model.uniq.pluck (: ให้คะแนน)
kakubei

6
pluckเป็น Rails> บริสุทธิ์วิธี 3.2 ซึ่งไม่มีการพึ่งพา Ruby 1.9.x
Daniel Rikowski

34

หากคุณต้องการเลือกเขตข้อมูลพิเศษ:

Model.select('DISTINCT ON (models.ratings) models.ratings, models.id').map { |m| [m.id, m.ratings] }

1
select extra fields<3 <3
cappie013

27
Model.uniq.pluck(:rating)

# SELECT DISTINCT "models"."rating" FROM "models"

นี่คือข้อดีของการไม่ใช้สตริง sql และไม่สร้างอินสแตนซ์โมเดล


3
สิ่งนี้ทำให้เกิดข้อผิดพลาดกับ Rails 5.1 / AR 5.1 => วิธีที่ไม่ได้กำหนด `uniq '
Graham Slick


5
Model.select(:rating).distinct

2
นี่เป็นคำตอบที่ถูกต้องอย่างเป็นทางการเท่านั้นที่มีประสิทธิภาพสุดยอด แม้ว่าการเพิ่ม.pluck(:rating)ท้ายจะทำให้สิ่งที่ OP ต้องการ
Sheharyar

5

ถ้าฉันไปถูกทางแล้ว:

แบบสอบถามปัจจุบัน

Model.select(:rating)

ส่งคืนอาร์เรย์ของวัตถุและคุณได้เขียนแบบสอบถาม

Model.select(:rating).uniq

uniq ถูกนำไปใช้กับอาร์เรย์ของวัตถุและแต่ละวัตถุมีรหัสที่ไม่ซ้ำกัน uniq ทำงานได้อย่างถูกต้องเพราะแต่ละวัตถุในอาร์เรย์เป็น uniq

มีหลายวิธีในการเลือกการให้คะแนนที่แตกต่าง:

Model.select('distinct rating').map(&:rating)

หรือ

Model.select('distinct rating').collect(&:rating)

หรือ

Model.select(:rating).map(&:rating).uniq

หรือ

Model.select(:name).collect(&:rating).uniq

อีกสิ่งหนึ่งแบบสอบถามแรกและครั้งที่สอง: ค้นหาข้อมูลที่แตกต่างจากแบบสอบถาม SQL

ข้อความค้นหาเหล่านี้จะถือว่าเป็น "london" และ "london" เหมือนกันนั่นหมายความว่าจะไม่สนใจช่องว่างนั่นคือเหตุผลที่จะเลือก 'london' หนึ่งครั้งในผลการสืบค้นของคุณ

ข้อความค้นหาที่สามและที่มา:

ค้นหาข้อมูลด้วยแบบสอบถาม SQL และใช้ข้อมูลที่แตกต่าง ruby ​​uniq mehtod ข้อความค้นหาเหล่านี้จะถือว่า "london" และ "london" ต่างกันนั่นคือสาเหตุที่จะเลือก 'london' และ 'london' ทั้งสองอย่างในผลการสืบค้นของคุณ

โปรดเลือกภาพที่แนบมาเพื่อความเข้าใจเพิ่มเติมและดูที่ "Toured / Awaiting RFP"

ป้อนคำอธิบายรูปภาพที่นี่


6
map& collectเป็นชื่อแทนสำหรับวิธีการเดียวกันโดยไม่จำเป็นต้องให้ตัวอย่างสำหรับทั้งสอง
Adam Lassek

4

คำตอบบางคำไม่คำนึงถึง OP ต้องการค่าอาร์เรย์

คำตอบอื่น ๆ ใช้ไม่ได้ผลหากแบบจำลองของคุณมีบันทึกหลายพันรายการ

ที่กล่าวว่าฉันคิดว่าคำตอบที่ดีคือ:

    Model.uniq.select(:ratings).map(&:ratings)
    => "SELECT DISTINCT ratings FROM `models` " 

เพราะก่อนอื่นคุณสร้างอาร์เรย์ของ Model (ด้วยขนาดที่ลดลงเนื่องจากการเลือก) จากนั้นคุณจะแยกเฉพาะแอททริบิวที่โมเดลที่เลือกมี (การให้คะแนน)


3

หากใครกำลังมองหาสิ่งเดียวกันกับ Mongoid นั่นคือ

Model.distinct(:rating)

อันนี้ไม่ทำงานตอนนี้มันจะคืนค่าทวีคูณ
EUPHORAY

ไม่ได้กลับชัดเจน
dowi

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