ActiveRecord: ขนาดเทียบกับจำนวน


201

ในทางรถไฟแล้วคุณจะพบจำนวนของระเบียนโดยใช้ทั้งสองและModel.size Model.countหากคุณกำลังจัดการกับข้อความค้นหาที่ซับซ้อนมากขึ้นจะมีข้อได้เปรียบอะไรบ้างหากใช้วิธีหนึ่งกับอีกวิธีหนึ่ง แตกต่างกันอย่างไร

ตัวอย่างเช่นฉันมีผู้ใช้ที่มีรูปถ่าย หากฉันต้องการแสดงตารางผู้ใช้และจำนวนภาพถ่ายที่มีพวกเขาจะเรียกใช้อินสแตนซ์จำนวนมากที่user.photos.sizeเร็วขึ้นหรือช้าลงกว่าเดิมuser.photos.countหรือไม่

ขอบคุณ!

คำตอบ:


344

คุณควรอ่านว่ามันยังใช้ได้อยู่

คุณจะปรับฟังก์ชั่นที่คุณใช้ขึ้นอยู่กับความต้องการของคุณ

โดยทั่วไป:

  • ถ้าคุณโหลดรายการทั้งหมดแล้วพูดUser.allจากนั้นคุณควรใช้lengthเพื่อหลีกเลี่ยงการสอบถามฐานข้อมูลอื่น

  • หากคุณไม่ได้โหลดอะไรให้ใช้countเพื่อสร้างคิวรีการสืบค้นบน db ของคุณ

  • หากคุณไม่ต้องการกังวลกับข้อควรพิจารณาเหล่านี้ให้ใช้วิธีsizeที่จะปรับเปลี่ยน


35
หากsizeปรับให้เข้ากับสถานการณ์แล้วสิ่งที่จำเป็นต้องมีlengthและcountทั้งหมด?
sscirrus

27
@sscirus - เพื่อให้sizeสามารถโทรหาพวกเขาเมื่อคุณโทรไปที่size(หลังจากที่กำหนดว่าจะโทรหนึ่ง)
Batkins

35
อย่างไรก็ตามโปรดระมัดระวังด้วยการกำหนดขนาดเริ่มต้น ตัวอย่างเช่นถ้าคุณสร้างบันทึกใหม่โดยไม่ต้องผ่านความสัมพันธ์เช่นComment.create(post_id: post.id)คุณpost.comments.sizeจะไม่เป็นปัจจุบันในขณะที่post.comments.countจะ ดังนั้นควรระวัง
mrbrdo

14
นอกจากนี้หากคุณสร้างวัตถุหลายรายการผ่านความสัมพันธ์: company.devices.build(:name => "device1"); company.devices.build(:name => "device2")แล้วcompany.devices.sizeและ.lengthจะรวมจำนวนวัตถุที่คุณสร้าง แต่ยังไม่ได้บันทึก.countจะรายงานเฉพาะจำนวนจากฐานข้อมูล
Shawn J. Goff

6
@sscirrus ขนาดเป็นคำสั่งที่อันตรายเนื่องจากเป็นแบบอัตโนมัติบางครั้งคุณต้องการสอบถาม db อีกครั้ง
Alex C

79

ตามที่คำตอบอื่น ๆ ระบุไว้:

  • countจะทำการCOUNTสืบค้นSQL
  • length จะคำนวณความยาวของอาร์เรย์ผลลัพธ์
  • size จะพยายามเลือกที่เหมาะสมที่สุดของทั้งสองเพื่อหลีกเลี่ยงการค้นหามากเกินไป

แต่มีอีกสิ่งหนึ่งคือ เราสังเกตเห็นกรณีที่sizeการกระทำที่แตกต่างกันไปcount/ lengthทั้งหมดและฉันคิดว่าฉันจะแบ่งปันเพราะมันเป็นเรื่องยากที่จะมองข้าม

  • ถ้าคุณใช้:counter_cacheในhas_manyสมาคมsizeจะใช้ Cached นับโดยตรงและไม่ทำให้การสอบถามพิเศษที่ทุกคน

    class Image < ActiveRecord::Base
      belongs_to :product, counter_cache: true
    end
    
    class Product < ActiveRecord::Base
      has_many :images
    end
    
    > product = Product.first  # query, load product into memory
    > product.images.size      # no query, reads the :images_count column
    > product.images.count     # query, SQL COUNT
    > product.images.length    # query, loads images into memory

พฤติกรรมนี้ได้รับการบันทึกไว้ในRails Guideแต่ฉันไม่ได้รับมันในครั้งแรกหรือลืมไปเลย


ในความเป็นจริงก่อนที่จะมีรถไฟ 5.0.0.beta1 พฤติกรรมนี้จะถูกเรียกแม้ว่าจะมี_countคอลัมน์ (ไม่มีcounter_cache: trueคำสั่งในการเชื่อมโยง) สิ่งนี้ได้รับการแก้ไขแล้วในgithub.com/rails/rails/commit/e0cb21f5f7
cbliard

8

บางครั้งsize"เลือกผิด" และคืนค่าแฮช (ซึ่งเป็นสิ่งที่countจะทำ)

ในกรณีที่ใช้lengthจะได้รับจำนวนเต็มแทนกัญชา


ฉันใช้ '.size' ในคอลเลกชันจากตัวอย่าง has_many และแม้ว่าจะมีหนึ่งระเบียนในคอลเลกชันขนาดก็ส่งคืน '0' การใช้. count ส่งคืนค่าที่ถูกต้องของ '1'
admazzola

4

TL; DR

  • หากคุณรู้ว่าคุณไม่จำเป็นต้องใช้ข้อมูล countถ้าคุณรู้ว่าคุณจะไม่ถูกต้องการใช้ข้อมูล
  • หากคุณรู้ว่าคุณจะใช้หรือเคยใช้งานข้อมูล lengthถ้าคุณรู้ว่าคุณจะใช้หรือมีการใช้การใช้ข้อมูล
  • หากคุณไม่ทราบว่ากำลังทำอะไรอยู่ให้ใช้size...

นับ

มีมติให้ส่ง Select count(*)...มติแบบสอบถามไปยังฐานข้อมูล วิธีที่จะไปหากคุณไม่ต้องการข้อมูล แต่เป็นเพียงการนับ

ตัวอย่าง: จำนวนข้อความใหม่องค์ประกอบทั้งหมดเมื่อแสดงเฉพาะหน้าเว็บเท่านั้นเป็นต้น

ความยาว

โหลดข้อมูลที่ต้องการเช่นแบบสอบถามตามต้องการจากนั้นนับข้อมูล วิธีที่จะไปหากคุณกำลังใช้ข้อมูล

ตัวอย่าง: สรุปตารางที่เต็มชื่อเรื่องของข้อมูลที่แสดง ฯลฯ

ขนาด

มันตรวจสอบว่าข้อมูลถูกโหลด (เช่นแล้วในราง) ถ้าเป็นเช่นนั้นเพียงแค่นับมันมิฉะนั้นจะเรียกนับ (รวมถึงข้อผิดพลาดที่กล่าวถึงแล้วในรายการอื่น ๆ )

def size
  loaded? ? @records.length : count(:all)
end

มีปัญหาอะไร?

เพื่อที่คุณจะได้กดปุ่ม DB สองครั้งหากคุณไม่ทำตามลำดับที่ถูกต้อง (เช่นหากคุณแสดงจำนวนองค์ประกอบในตารางที่ด้านบนของตารางที่แสดงผลจะมีการโทร 2 ครั้งที่ส่งไปยัง DB)


3

กลยุทธ์ต่อไปนี้จะทำการเรียกไปยังฐานข้อมูลเพื่อทำการCOUNT(*)สืบค้น

Model.count

Model.all.size

records = Model.all
records.count

ต่อไปนี้ไม่ได้มีประสิทธิภาพเท่าที่จะโหลดระเบียนทั้งหมดจากฐานข้อมูลลงใน Ruby ซึ่งนับขนาดของคอลเลกชัน

records = Model.all
records.size

หากรุ่นของคุณมีการเชื่อมโยงและคุณต้องการค้นหาจำนวนของวัตถุที่เป็นของ (เช่น@customer.orders.size) คุณสามารถหลีกเลี่ยงการสืบค้นฐานข้อมูล (อ่านดิสก์) ใช้ตัวนับแคชและ Rails จะทำให้ค่าแคชเป็นปัจจุบันและส่งคืนค่านั้นตามsizeวิธีการ


2
ทั้งสองModel.all.sizeและModel.all.countสร้างcountแบบสอบถามใน Rails 4 ขึ้นไป ข้อได้เปรียบที่แท้จริงของsizeมันคือมันไม่ได้สร้างคิวรีแบบนับถ้ามีการโหลดการเชื่อมโยงแล้ว ใน Rails 3 และต่ำกว่าฉันเชื่อว่าModel.allไม่ใช่ความสัมพันธ์ดังนั้นบันทึกทั้งหมดจึงโหลดเรียบร้อยแล้ว คำตอบนี้อาจล้าสมัยและฉันขอแนะนำให้ลบ
Damon Aw

1

ฉันแนะนำให้ใช้ฟังก์ชั่นขนาด

class Customer < ActiveRecord::Base
  has_many :customer_activities
end

class CustomerActivity < ActiveRecord::Base
  belongs_to :customer, counter_cache: true
end

พิจารณาสองรุ่นนี้ ลูกค้ามีกิจกรรมลูกค้ามากมาย

หากคุณใช้ a: counter_cache กับการเชื่อมโยง has_many ขนาดจะใช้การนับแคชโดยตรงและไม่ต้องสืบค้นเพิ่มเติม

ลองพิจารณาตัวอย่างหนึ่ง: ในฐานข้อมูลของฉันลูกค้ารายหนึ่งมีกิจกรรมลูกค้า 20,000 รายการและฉันพยายามนับจำนวนบันทึกกิจกรรมลูกค้าของลูกค้านั้นด้วยวิธีนับความยาวและขนาดแต่ละวิธี ที่นี่ด้านล่างรายงานมาตรฐานของวิธีการเหล่านี้ทั้งหมด

            user     system      total        real
Count:     0.000000   0.000000   0.000000 (  0.006105)
Size:      0.010000   0.000000   0.010000 (  0.003797)
Length:    0.030000   0.000000   0.030000 (  0.026481)

ดังนั้นฉันพบว่าการใช้: counter_cache Size เป็นตัวเลือกที่ดีที่สุดในการคำนวณจำนวนเรคคอร์ด

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