slothis · ไทย

นับ QuerySet ใน Django: ใช้ count() หรือ len() ดี?

2020-12-05 · อัปเดต: 2026-08-11 · ต้นฉบับ (ภาษาเกาหลี)

บทความนี้แปลจากต้นฉบับภาษาเกาหลีโดยใช้ AI ช่วย อ่านต้นฉบับภาษาเกาหลี →
โลโก้อย่างเป็นทางการของเฟรมเวิร์ก Django เขียนด้วยตัวพิมพ์เล็กสีขาวบนพื้นหลังสีเขียวเข้ม

ช่วงนี้ผมได้คอมเมนต์ตอนรีวิวโค้ดว่าให้เปลี่ยนการนับจำนวนแถว (row) จากที่ใช้ len() มาเป็น count แทน ด้วยความที่ยังเป็นมือใหม่เรื่อง ORM และ Django ผมเลยลองไปค้นใน StackOverflow ดู ปรากฏว่าส่วนใหญ่แนะนำให้ใช้ count ในการเช็คจำนวนแถวกันทั้งนั้น งั้นเรามาดูกันดีกว่าครับว่าเหตุผลคืออะไร

สมมติว่าเราอยากรู้จำนวน User ในระบบของเราตอนนี้

(ถ้าทำเล่นๆ ในระบบที่มียูสเซอร์เป็นล้านหรือสิบล้าน ระวังจะโดนพี่ๆ DevOps ของบริษัทเรียกไปคุยนะครับ)

qs = User.objects.all()\n\n# มีวิธีใช้ count() ของ queryset\ndef user_count_by_count():\n    return qs.count()\n\n# วิธีเช็คด้วย len\ndef user_count_by_len():\n    return len(qs)

ในกรณีของ count() คำสั่ง SELECT COUNT(*) FROM User จะถูกรันใน RDBMS แล้วส่งแค่ค่า count กลับมาที่ Python

ส่วน len() จะรันคำสั่ง SELECT * FROM User ใน RDBMS ก่อน แล้วส่งผลลัพธ์ทั้งหมดกลับมาให้ Python นับจำนวนด้วย len()

หลายคนมักจะบอกว่า ถ้าต้องนับจำนวนแถว ให้ใช้ count() เหตุผลก็คือถ้าเราพยายามใช้ len() แทน ตอน fetch ข้อมูลจะใช้เวลา O(N) นอกจากนี้ การเอาผลลัพธ์ไป เก็บไว้ใน memory ของเว็บเซิร์ฟเวอร์ก็จะกินพื้นที่ O(N) และเวลาที่ใช้ในการก็อปปี้ข้อมูลก็จะทำให้ เสียเวลาเพิ่มอีก O(N) แถมยังมีเวลาที่ต้องใช้รัน len() อีกต่างหาก ถึงแม้กระบวนการเหล่านี้จะขึ้นอยู่กับอินฟราสตรัคเจอร์ด้วย แต่เขาว่ากันว่า count จะเร็วกว่าประมาณ 2 เท่าครับ

แต่ก็มีบางกรณีที่ใช้ len() แล้วดีกว่า count เหมือนกัน

qs = User.objects.all()\n\ndef prefer_len_to_count():\n    # สมมติว่าในข้อมูล User มีฟิลด์ telephone อยู่ แล้วเราดึงข้อมูลนั้นมารวมเป็นลิสต์\n    telephones = [p.telephone for p in qs]\n    # เอาข้อมูลนั้นไปทำอะไรสักอย่าง...\n    ...\n    # หาจำนวนของ qs (ส่วนจะเอาไปทำอะไรก็ลองคิดดูนะครับ)\n    len(qs)

ถ้าเป็นสถานการณ์ที่เราต้อง fetch ตัว queryset มาใช้งานแบบด้านบน การใช้ len จะได้เปรียบกว่านิดหน่อย เหตุผลก็คือถ้าใช้ count() เราจะต้องเข้าไปดึงข้อมูลใน RDBMS ถึงสองรอบ (fetch หนึ่งรอบ และ count() อีกหนึ่งรอบ)

อาจจะเป็นคำพูดที่ทำให้โดนพี่ๆ DevOps ดุเอาได้ แต่ ถ้าไม่ได้นับแถวที่เยอะมากๆ (ซึ่งเกณฑ์ความเยอะก็ขึ้นอยู่กับสภาพอินฟราสตรัคเจอร์อีกที) จะใช้ count ไปเลยก็ไม่มีปัญหาครับ

แน่นอนว่าใน template ก็แนะนำให้ใช้ count เหมือนกัน

การใช้ count แบบด้านล่างนี้

{{ some_queryset.count }}

จะถูกแนะนำมากกว่าการใช้ len แบบด้านล่างครับ

{{ len(some_queryset) }}

อ้างอิง: stackoverflow.com/questions/14327036/count-vs-len-on-a-django-queryset


#django #Django