มีวิธีที่มีประสิทธิภาพมากกว่านี้ในการเขียนโค้ดด้วย ActiveRecord หรือไม่

ฉันมีสามตาราง:

  1. ผู้ใช้
  2. คำถาม
  3. User_คำถาม

    1.คำถามของผู้ใช้มีคอลัมน์ user_id, question_id และ answer

ฉันต้องการค้นหาคำถามสุ่มที่ไม่ได้รับคำตอบ และ จึงไม่มีแถวในตาราง user_questions

หากทุกคำถามได้รับคำตอบแล้ว ให้ส่งคืนคำถามแบบสุ่ม

ฉันได้รับแจ้งว่าสิ่งนี้สามารถทำได้โดยใช้ OUTER JOIN แต่ฉันเป็น SQL noob และฉันไม่แน่ใจว่าจะทำอย่างไรใน Rails
นี่คือสิ่งที่ฉันมี:

def next_question          
  q = Question.all - Question.joins(:user_questions).where
       (user_questions: { user_id: user_id })
  q = Question.all if q.empty?
  return q[rand(q.size)]
end  

person modernserf    schedule 07.03.2012    source แหล่งที่มา


คำตอบ (2)


แทบจะไม่มีเหตุผลที่ดีในการเรียกเมธอด all ในคลาสโมเดลเลย วิธีนี้จะโหลดทุกบันทึกในฐานข้อมูลประเภทนั้นลงในหน่วยความจำ และหากคุณไม่มั่นใจว่านี่เป็นบันทึกชุดเล็กๆ คุณก็อาจทำให้ระบบทั้งหมดของคุณค้างได้ ถึงกระนั้นก็ตาม การโหลด ทุกอย่าง ในรูปแบบที่แย่มาก และจากนั้นก็เลือกสิ่งหนึ่งแล้วทิ้งส่วนที่เหลือไป มันเหมือนกับการสั่งซื้อสินค้าจาก Amazon ทุกรายการ เลือกปากกาที่คุณต้องการ และทิ้งสิ่งของที่เหลือลงถังขยะ

สิ่งที่คุณอาจต้องการคือสิ่งที่คุณสุ่มเลือกบันทึก หนึ่ง ที่ยังไม่ได้กำหนด นั่นอาจมีลักษณะเช่นนี้:

Question.where('id NOT IN (SELECT question_id FROM user_questions WHERE user_id=?)', user_id).order('RAND()').first

ปัญหาของ JOIN คือคุณจะพบระเบียนที่ตรงกันในตาราง user_questions ไม่ใช่ตารางผกผัน

ข้อความค้นหานี้สันนิษฐานว่าจำนวนคำถามที่ผู้ใช้ตอบมีจำนวนค่อนข้างน้อย หรือ NOT IN อาจมีราคาแพงกว่ามาก

person tadman    schedule 07.03.2012
comment
SQL เวอร์ชันต่างๆ ดูเหมือนจะต้องการ 'RANDOM()' เวอร์ชันที่แตกต่างกัน แต่ใช่แล้ว - person modernserf; 07.03.2012

ใช่ คุณสามารถใช้ LEFT OUTER JOIN สำหรับสิ่งนี้ INNER JOIN ปกติจะรวมเฉพาะแถวที่ตรงกับเงื่อนไขการรวมเท่านั้น LEFT JOIN จะรวมแถวที่ตรงกันและจะแยกแถวที่ไม่ตรงกันออกโดยการใส่ NULL ลงในคอลัมน์ทั้งหมด (เอกสาร PostgreSQL มีคำอธิบายที่สมเหตุสมผล)

ดังนั้น คุณเข้าร่วม LEFT JOIN แล้วมองหาแถวที่ไม่ตรงกันโดยมองหา NULL SQL จะมีลักษณะดังนี้:

select ...
from questions
left outer join user_questions on questions.id = user_questions.question_id
where user_questions.question_id is null

นั่นจะทำให้คุณมีคำถามทั้งหมดที่ไม่ได้รับคำตอบ ใน ActiveRecord คุณสามารถทำสิ่งนี้:

Question.joins('left outer join user_questions on question.id = user_questions.question_id')
        .where(:user_questions => { :question_id => nil })
        .limit(1)

คุณอาจต้องการเล่นกับการเรียงลำดับแบบสุ่มเช่นกัน แต่นั่นควรช่วยให้คุณเริ่มต้นได้ หากไม่ตรงกัน คุณสามารถสุ่มคำถามได้ดังนี้:

n = Questions.count
q = Questions.offset(rand(n)).limit(1)

คุณสามารถทำข้างต้นด้วย Questions.order('random()').limit(1) ได้เช่นกัน แต่ ORDER BY random() อาจไม่เป็นที่พอใจสำหรับฐานข้อมูลขนาดใหญ่ การนับควรจะค่อนข้างเร็ว ดังนั้นการสุ่มเลือกใน Ruby ควรจะเร็วพอ และมันจะไม่ทำให้ฐานข้อมูลของคุณเกลียดคุณ

นอกจากนี้ โปรดดูเอกสารประกอบเพื่อดูวิธีจัดระเบียบ joins นั้นด้วย ฉันกำลังทำงานกับฐานข้อมูลที่มีโครงสร้างที่ไม่ได้มาตรฐานเล็กน้อย ดังนั้นฉันจึงต้องทำสิ่งต่าง ๆ เป็นเวลานานในบางครั้ง ActiveRecord ปฏิเสธที่จะทำการ LEFT JOIN ให้ฉัน เว้นแต่ฉันจะสะกดเป็น YMMV

person mu is too short    schedule 07.03.2012