MySQL จำกัด & OFFSET พร้อมตัวอย่าง

⚡ สรุปอย่างชาญฉลาด

การขอ MySQL คำสั่ง LIMIT จำกัดจำนวนแถวที่คำสั่งค้นหาจะส่งคืน และค่า OFFSET จะกำหนดว่าผลลัพธ์จะเริ่มต้นจากแถวใด เมื่อใช้ร่วมกันจะทำให้ชุดผลลัพธ์มีขนาดเล็ก ทำให้หน้าเว็บโหลดเร็ว และรองรับการแบ่งหน้าแบบทีละรายการ

  • 🔢 พฤติกรรมหลัก: คำสั่ง LIMIT N จะส่งคืนข้อมูลไม่เกิน N แถว หากตารางมีจำนวนแถวน้อยกว่า N แถว ระบบจะส่งคืนข้อมูลทั้งหมดโดยไม่มีข้อผิดพลาด
  • 0️⃣ กรณีที่ศูนย์: คำสั่ง LIMIT 0 จะไม่ส่งคืนแถวใดๆ ทำให้เป็นวิธีที่ประหยัดในการตรวจสอบเมตาเดต้าของคอลัมน์
  • 📍 ไวยากรณ์ออฟเซ็ต: คำสั่ง LIMIT 1, 2 จะข้ามหนึ่งแถวและส่งคืนค่าสองแถว ดังนั้นค่าออฟเซ็ตจะถูกเขียนก่อน แล้วจึงเขียนจำนวนแถวเป็นลำดับถัดไป
  • 📄 สูตรการแบ่งหน้า: ค่า OFFSET เท่ากับขนาดหน้าคูณด้วยหมายเลขหน้าลบหนึ่ง ซึ่งจะแปลงชุดผลลัพธ์ให้เป็นหน้าที่มีหมายเลขกำกับ
  • ↕️ ความสัมพันธ์ของลำดับการสั่งซื้อ: หากไม่มีคำสั่งจ่ายภายใน MySQL อาจส่งคืนแถวที่แตกต่างกันในแต่ละครั้งที่ทำงาน ดังนั้นฟังก์ชัน LIMIT จึงให้ผลลัพธ์ที่แน่นอนได้ก็ต่อเมื่อมีการเรียงลำดับอย่างชัดเจนเท่านั้น
  • ⚙️ หลักฐานสนับสนุนคำแถลง: คำสั่ง LIMIT ยังจำกัดจำนวนแถวที่ได้รับผลกระทบจากคำสั่ง UPDATE และ DELETE ซึ่งช่วยป้องกันตารางขนาดใหญ่จากการเขียนข้อมูลโดยไม่จำกัดจำนวน
  • 🐢 ข้อควรระวังด้านประสิทธิภาพ: การชดเชยขนาดใหญ่ทำให้ MySQL อ่านและลบแถวที่ข้ามไปทั้งหมด เพื่อให้หน้าเว็บที่อยู่ลึกเข้าไปเติบโตช้าลง

MySQL LIMIT และ OFFSET

คีย์เวิร์ด LIMIT คืออะไรใน MySQL?

การขอ LIMIT คีย์เวิร์ดนี้จำกัดจำนวนแถวที่แสดงในผลลัพธ์การค้นหา สามารถใช้ได้กับคำสั่ง SELECT, UPDATE และ DELETE ดังนั้นจึงจำกัดจำนวนแถวที่การค้นหาอ่าน รวมถึงจำนวนแถวที่การเขียนข้อมูลมีผลด้วย

รูปแบบการใช้งานของคำสั่ง LIMIT มีดังนี้

SELECT {fieldname(s) | *} FROM tableName(s) [WHERE condition] LIMIT N;

ที่นี่

  • “เลือก {ชื่อฟิลด์ | *} จากชื่อตาราง” คือ คำสั่ง SELECT มีช่องที่เราต้องการส่งคืนในการสืบค้นของเรา
  • “[เงื่อนไขอยู่ที่ไหน]” เป็นตัวเลือกเสริม แต่หากระบุไว้ จะเป็นการกำหนดตัวกรองสำหรับชุดผลลัพธ์ WHERE ข้อ ฟังก์ชันนี้จะถูกนำไปใช้ก่อนฟังก์ชัน LIMIT ดังนั้นการกรองจะเกิดขึ้นก่อน และขีดจำกัดจะถูกนำไปใช้กับส่วนที่เหลืออยู่
  • “ลิมิต เอ็น” คือคำหลัก และ N N คือจำนวนใดๆ ที่เริ่มต้นจาก 0 การกำหนดค่า 0 เป็นค่าจำกัดจะไม่แสดงผลลัพธ์ใดๆ เลย การกำหนดค่าเช่น 5 จะแสดงผลลัพธ์ห้ารายการ หากตารางมีข้อมูลน้อยกว่า N ระบบจะส่งคืนข้อมูลทั้งหมดและไม่แสดงข้อผิดพลาดใดๆ

ไวยากรณ์นั้นสั้น แต่เหตุผลที่มันมีอยู่นั้นควรค่าแก่การกล่าวถึงก่อนที่จะยกตัวอย่าง

เหตุใดเราจึงควรใช้คีย์เวิร์ด LIMIT?

สมมติว่าเรากำลังพัฒนา...ping แอปพลิเคชันนี้ทำงานอยู่บนฐานข้อมูล myflixdb นักออกแบบระบบขอให้เราจำกัดจำนวนระเบียนที่แสดงบนหน้าเว็บไว้ที่ 20 รายการ เพื่อแก้ปัญหาการโหลดช้า เราจะพัฒนาระบบให้ตรงตามข้อกำหนดนี้ได้อย่างไร?

คำสั่ง LIMIT ช่วยจัดการสถานการณ์นี้ได้อย่างแม่นยำ แทนที่จะดึงข้อมูลทุกแถวเข้ามาในแอปพลิเคชันแล้วทิ้งส่วนใหญ่ไป การค้นหาจะส่งคืนข้อมูล 20 รายการต่อหน้า และฐานข้อมูลจะเป็นผู้ดำเนินการส่วนที่เหลือ ซึ่งมีข้อดีสามประการดังนี้

  • ตอบสนองเร็วขึ้น: มีการอ่านข้อมูลจากดิสก์น้อยลง และมีการส่งข้อมูลผ่านเครือข่ายน้อยลง
  • ลดการใช้หน่วยความจำ: แอปพลิเคชันนี้เก็บข้อมูลเพียงหน้าเดียว ไม่ใช่ทั้งตาราง
  • เซเฟอร์เขียนว่า: ข้อจำกัดในการอัปเดตหรือ ลบ คำสั่งดังกล่าวจำกัดจำนวนแถวที่ข้อผิดพลาดอาจส่งผลกระทบได้

MySQL ตัวอย่างคำสั่ง LIMIT

ตัวอย่างด้านล่างนี้ทำงานกับตาราง members ของฐานข้อมูล myflixdb ตัวอย่างแรกจะส่งคืนสองแถวและไม่มีอะไรเพิ่มเติม

SELECT * FROM members LIMIT 2;
หมายเลขสมาชิก เต็ม_ชื่อ เพศ วันเกิด วันที่ลงทะเบียน ที่อยู่ทางกายภาพ_ รหัสไปรษณีย์ หมายเลขติดต่อ อีเมล หมายเลขบัตรเครดิต
1 เจเน็ต โจนส์ หญิง 21-07-1980 NULL ถนนสายแรก แปลงที่ 4 กระเป๋าส่วนตัว 0759 253 542 janetjones@yagoo.cm NULL
2 เจเน็ต สมิธ โจนส์ หญิง 23-06-1980 NULL เมลโรส 123 NULL NULL jj@fstreet.com NULL

จากผลลัพธ์ข้างต้น แสดงให้เห็นว่ามีสมาชิกเพียงสองคนเท่านั้นที่ได้รับเลือกกลับมา

ดึงรายชื่อสมาชิกสิบ (10) คนจากฐานข้อมูล

สมมติว่าเราต้องการรายชื่อสมาชิก 10 คนแรกที่ลงทะเบียนจากฐานข้อมูล Myflix สคริปต์ด้านล่างนี้จะขอรายชื่อเหล่านั้น

SELECT * FROM members LIMIT 10;

เมื่อรันสคริปต์จะได้ผลลัพธ์ดังที่แสดงด้านล่าง

หมายเลขสมาชิก เต็ม_ชื่อ เพศ วันเกิด วันที่ลงทะเบียน ที่อยู่ทางกายภาพ_ รหัสไปรษณีย์ หมายเลขติดต่อ อีเมล หมายเลขบัตรเครดิต
1 เจเน็ต โจนส์ หญิง 21-07-1980 NULL ถนนสายแรก แปลงที่ 4 กระเป๋าส่วนตัว 0759 253 542 janetjones@yagoo.cm NULL
2 เจเน็ต สมิธ โจนส์ หญิง 23-06-1980 NULL เมลโรส 123 NULL NULL jj@fstreet.com NULL
3 โรเบิร์ต ฟิล ชาย 12-07-1989 NULL 3 ถนน 34 NULL 12345 rm@tstreet.com NULL
4 กลอเรียวิลเลียมส์ หญิง 14-02-1984 NULL 2 ถนน 23 NULL NULL NULL NULL
5 ลีโอนาร์ด ฮอฟสตัดเตอร์ ชาย NULL NULL วู้ดเครสต์ NULL 845738767 NULL NULL
6 เชลดอนคูเปอร์ ชาย NULL NULL วู้ดเครสต์ NULL 976736763 NULL NULL
7 ราชสีห์ คูทรพาลี ชาย NULL NULL วู้ดเครสต์ NULL 938867763 NULL NULL
8 เลสลี่ วิงเคิล ชาย 14-02-1984 NULL วู้ดเครสต์ NULL 987636553 NULL NULL
9 ฮาวเวิร์ด โวโลวิทซ์ ชาย 24-08-1981 NULL SouthPark PO Box 4563 987786553 lwolowitz[at]email.me NULL

มีการแสดงผลข้อมูลเพียง 9 รายการ เนื่องจากค่า N ในเงื่อนไข LIMIT มากกว่าจำนวนระเบียนในตาราง การขอข้อมูล 9 แถวโดยตรงก็ให้ผลลัพธ์ชุดเดียวกัน

SELECT * FROM members LIMIT 9;

💡 เคล็ดลับ: คำสั่ง LIMIT จะเลือกแถวตามลำดับใดก็ตามที่เซิร์ฟเวอร์สร้างขึ้น เพิ่ม สั่งโดย เงื่อนไขนี้ใช้เมื่อใดก็ตามที่เอกลักษณ์ของแถวมีความสำคัญ มิฉะนั้น "สมาชิก 10 คนแรก" จะไม่รับประกันว่าจะหมายถึงบุคคล 9 คนเดียวกันสองครั้ง

การจำกัดจำนวนแถวเป็นส่วนแรกของฟีเจอร์นี้ การเลือกจุดเริ่มต้นของหน้าต่างเป็นส่วนที่สอง

การใช้ค่า OFFSET ในคำสั่ง LIMIT

การขอ OFFSET ค่า (value) มักใช้ร่วมกับคีย์เวิร์ด LIMIT โดยจะระบุว่าเซิร์ฟเวอร์จะเริ่มดึงข้อมูลจากแถวใด ดังนั้นแถวก่อนหน้านั้นจะถูกข้ามไป

สมมติว่าเราต้องการสมาชิกจำนวนจำกัด โดยเริ่มจากตรงกลางตาราง สคริปต์ด้านล่างจะเริ่มต้นที่แถวที่สองและจำกัดผลลัพธ์ไว้ที่สองรายการ

SELECT * FROM `members` LIMIT 1, 2;

ดำเนินการใน MySQL ม้านั่งทำงานของช่างเครื่อง เมื่อเปรียบเทียบกับ myflixdb จะได้ผลลัพธ์ดังต่อไปนี้

หมายเลขสมาชิก เต็ม_ชื่อ เพศ วันเกิด วันที่ลงทะเบียน ที่อยู่ทางกายภาพ_ รหัสไปรษณีย์ หมายเลขติดต่อ อีเมล หมายเลขบัตรเครดิต
2 เจเน็ต สมิธ โจนส์ หญิง 23-06-1980 NULL เมลโรส 123 NULL NULL jj@fstreet.com NULL
3 โรเบิร์ต ฟิล ชาย 12-07-1989 NULL 3 ถนน 34 NULL 12345 rm@tstreet.com NULL

โปรดทราบว่าตรงนี้ ออฟเซ็ต = 1ดังนั้น แถวที่ 2 จึงเป็นแถวแรกที่แสดงผล และ ลิมิต = 2ดังนั้นจึงได้ผลลัพธ์กลับมาเพียง 2 รายการเท่านั้น

ในรูปแบบที่มีสองอาร์กิวเมนต์ ค่าออฟเซ็ตจะถูกเขียนไว้ก่อน แล้วตามด้วยจำนวนแถว ซึ่งอาจทำให้เกิดการสลับลำดับโดยไม่ตั้งใจได้ง่าย MySQL นอกจากนี้ยังยอมรับรูปแบบที่ชัดเจนซึ่งช่วยขจัดความกำกวม และเป็นรูปแบบที่ควรเลือกใช้ในโค้ดใหม่

SELECT * FROM `members` LIMIT 2 OFFSET 1;

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

วิธีการแบ่งหน้าผลลัพธ์การค้นหาด้วย LIMIT และ OFFSET

การแบ่งหน้าจะแบ่งชุดผลลัพธ์ขนาดใหญ่เป็นหน้าที่มีหมายเลขกำกับ และคำสั่ง LIMIT ร่วมกับ OFFSET คือกลไกที่ใช้ในการแบ่งหน้า ค่าสองค่าที่ขับเคลื่อนการร้องขอหน้าแต่ละครั้ง ได้แก่ ขนาดของหน้า ซึ่งเป็นจำนวนระเบียนที่แสดงบนหนึ่งหน้าจอ และหมายเลขหน้าที่ผู้ใช้ร้องขอ

ค่าชดเชยได้มาจากการใช้สูตรเดียวในการคำนวณ

-- OFFSET = page_size * (page_number - 1)
SELECT membership_number, full_names
FROM members
ORDER BY membership_number ASC
LIMIT 20 OFFSET 0;   -- page 1

หน้าที่ 2 ยังคงใช้ขีดจำกัดเดิม แต่เลื่อนตำแหน่งไปข้างหน้าหนึ่งขนาดหน้า

SELECT membership_number, full_names
FROM members
ORDER BY membership_number ASC
LIMIT 20 OFFSET 20;  -- page 2

สามกฎสำคัญที่จะช่วยให้การแสดงรายการแบบแบ่งหน้าถูกต้องและรวดเร็ว

  1. เรียงลำดับเสมอ: การค้นหาแบบแบ่งหน้าโดยไม่ใช้ ORDER BY อาจแสดงเรคอร์ดเดียวกันในสองหน้าต่างกัน และซ่อนเรคอร์ดอื่นไว้อย่างสมบูรณ์ เนื่องจากเซิร์ฟเวอร์สามารถเปลี่ยนแปลงลำดับแถวระหว่างการเรียกใช้ได้
  2. เรียงลำดับตามคอลัมน์ที่ไม่ซ้ำกัน: หากมีค่าเท่ากันในคอลัมน์ที่ใช้เรียงลำดับ ลำดับของแถวที่มีค่าเท่ากันจะไม่แน่นอน การเรียงลำดับโดยใช้คีย์หลัก หรือการเพิ่มคีย์หลักเป็นตัวตัดสิน จะช่วยแก้ปัญหานี้ได้
  3. ดูรายละเอียดเพิ่มเติมในหน้าเว็บ: ชดเชย 100000 แรง MySQL อ่านข้อมูลหนึ่งแสนแถวแล้วทิ้งไป ก่อนจะส่งข้อมูลยี่สิบแถวถัดไปกลับมา เวลาตอบสนองจะเพิ่มขึ้นตามจำนวนหน้า

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

SELECT membership_number, full_names
FROM members
WHERE membership_number > 20      -- last id from the previous page
ORDER BY membership_number ASC
LIMIT 20;

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

ขีดจำกัดใน MySQL เทียบกับ TOP และ FETCH FIRST

คำสั่ง LIMIT ไม่ได้เป็นส่วนหนึ่งของภาษา SQL ทุกภาษา ซึ่งเป็นเรื่องสำคัญเมื่อต้องส่งคำสั่งค้นหาข้ามฐานข้อมูล MySQL, PostgreSQLและ SQLite ใช้คีย์เวิร์ด LIMIT ร่วมกัน SQL Server ใช้ TOP และ Oracle ใช้คำสั่ง FETCH FIRST มาตรฐาน ตารางด้านล่างเปรียบเทียบทั้งสามแบบ

ประโยค เครื่องยนต์ ตัวอย่าง ข้ามแถว
ขีดจำกัด … ออฟเซ็ต MySQL, PostgreSQL, SQLite SELECT * FROM members LIMIT 20 OFFSET 40; ใช่ครับ โดยใช้ค่า OFFSET
TOP SQL Server เลือก 20 อันดับแรก * จากสมาชิก; ไม่ ต้องใช้ OFFSET … FETCH
ดึงข้อมูลก่อน Oracleฐานข้อมูล Db2, SQL มาตรฐาน SELECT * FROM members FETCH FIRST 20 ROWS ONLY; ใช่แล้ว โดยใช้ OFFSET … ROWS

พฤติกรรมจะเหมือนกันในแต่ละกรณี คือ จำกัดจำนวนแถว และอาจข้ามแถวจำนวนหนึ่งก่อนก็ได้ ต่างกันแค่การสะกดคำเท่านั้น ดังนั้น คำสั่งค้นหาที่ต้องทำงานบนหลายเครื่องควรแยกส่วนการจำกัดจำนวนแถวออกจากส่วนอื่นในโค้ด แทนที่จะกระจายไปทั่ว

คำถามที่พบบ่อย

ใช่ ทั้งสองแบบรองรับการนับจำนวนแถวแบบธรรมดา เช่น ลบ จากสมาชิกจำกัดที่ 10 รูปแบบออฟเซ็ตที่มีสองอาร์กิวเมนต์ไม่ได้รับอนุญาต ดังนั้นจึงสามารถจำกัดได้เฉพาะจำนวนแถวที่ได้รับผลกระทบเท่านั้น

ค่าออฟเซ็ตนับจากศูนย์ ดังนั้น ออฟเซ็ต 0 จะเริ่มต้นที่แถวแรก และออฟเซ็ต 1 จะเริ่มต้นที่แถวที่สอง ส่วนจำนวนแถวนั้นเป็นปริมาณธรรมดาและอ่านได้เหมือนตัวเลขทั่วไป

เรียกใช้คำสั่ง SELECT COUNT(*) แยกต่างหาก โดยใช้เงื่อนไข WHERE เดียวกัน แต่ไม่ต้องใช้ LIMIT คำสั่ง COUNT จะบอกแอปพลิเคชันว่ามีกี่หน้า ในขณะที่คำสั่ง LIMIT จะส่งคืนแถวสำหรับหน้าปัจจุบัน

ใช่ บ่อยครั้ง ตัวอย่างเช่น ผู้ช่วย AI ภายในโปรแกรมไคลเอ็นต์ต่างๆ MySQL ม้านั่งทำงานของช่างเครื่อง เขียนคำสั่ง OFFSET ใหม่ให้เป็นคำสั่ง WHERE โดยใช้คีย์ที่พบครั้งล่าสุด ตรวจสอบให้แน่ใจว่าคอลัมน์ที่ใช้ในการเรียงลำดับมีค่าที่ไม่ซ้ำกันและมีการสร้างดัชนีไว้แล้ว ก่อนที่จะเชื่อถือการเขียนคำสั่งใหม่นี้

เนื่องจากคำสั่งที่สร้างขึ้นมักจะละเว้นคำสั่ง ORDER BY หากไม่มีการระบุการเรียงลำดับอย่างชัดเจน MySQL คำสั่ง LIMIT อาจส่งคืนแถวในลำดับใดก็ได้ ดังนั้นคำสั่ง LIMIT เดียวกันอาจให้ผลลัพธ์ตัวอย่างที่แตกต่างกันในแต่ละครั้ง คุณต้องเพิ่มการเรียงลำดับด้วยตนเอง

สรุปโพสต์นี้ด้วย: