ข้อมูล MySQL - วิธีที่ดีที่สุดในการใช้เพจ


209

แอพ iPhone ของฉันเชื่อมต่อกับบริการเว็บ PHP ของฉันเพื่อดึงข้อมูลจากฐานข้อมูล MySQL คำขอสามารถส่งคืนผลลัพธ์ 500 รายการ

วิธีที่ดีที่สุดในการปรับใช้เพจจิ้งและเรียกค้น 20 รายการพร้อมกันคืออะไร

สมมติว่าฉันได้รับโฆษณา 20 ชิ้นแรกจากฐานข้อมูลของฉัน ตอนนี้ฉันจะขอโฆษณา 20 รายการถัดไปได้อย่างไร

คำตอบ:


309

จากเอกสาร MySQL :

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

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

SELECT * FROM tbl LIMIT 5,10;  # Retrieve rows 6-15

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

SELECT * FROM tbl LIMIT 95,18446744073709551615;

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

SELECT * FROM tbl LIMIT 5;     # Retrieve first 5 rows

กล่าวอีกนัยหนึ่ง LIMIT Row_count เทียบเท่ากับ LIMIT 0, row_count


107
เมื่อใช้ LIMIT สำหรับการเพจคุณควรระบุ ORDER BY
Mark Byers

10
@shylent: ไม่มีอะไรผิดปกติกับการอ้างถึงเอกสาร แต่ฉันยอมรับว่าเขาควรได้กล่าวว่าเขากำลังคัดลอกเอกสารและให้ลิงก์ไปยังแหล่งต้นฉบับ นอกจากนี้ฉันยังแปลกใจที่เอกสารดังกล่าวจะมีตัวอย่างของการใช้ LIMIT โดยไม่มี ORDER BY ... ซึ่งดูเหมือนจะเป็นการกระตุ้นที่ไม่ดี หากไม่มีการสั่งซื้อโดยไม่มีการรับประกันว่าการสั่งซื้อจะเหมือนกันระหว่างการโทร
Mark Byers

13
อย่างไรก็ตามเมื่อให้เลขชุดผลลัพธ์ขนาดใหญ่ (และนั่นก็คือการแบ่งหน้า - แบ่งชุดผลลัพธ์ขนาดใหญ่เป็นชิ้นเล็ก ๆ ใช่มั้ย) คุณควรจำไว้ว่าถ้าคุณทำlimit X, Yสิ่งที่เกิดขึ้นคือแถว X + Y ถูกดึงออกมาแล้ว แถว X จากจุดเริ่มต้นจะถูกดร็อปและสิ่งที่เหลือจะถูกส่งคืน หากต้องการย้ำ: limit X, Yผลลัพธ์ในการสแกนแถว X + Y
shylent

7
ฉันไม่ชอบ LIMIT 95 ของคุณ, 18446744073709551615 ไอเดีย .. ลองดูที่OFFSET;-)
CharlesLeaf

5
สิ่งนี้ไม่ได้มีประสิทธิภาพเมื่อทำงานกับข้อมูลขนาดใหญ่ ตรวจสอบcodular.com/implementing-paginationสำหรับวิธี mutiple ซึ่งเหมาะสำหรับฉากที่เฉพาะเจาะจง
Amit

125

สำหรับประสิทธิภาพ 500 ระเบียนอาจไม่ใช่ปัญหา แต่ถ้าคุณมีหลายล้านระเบียนคุณสามารถใช้ WHERE clause เพื่อเลือกหน้าถัดไป:

SELECT *
FROM yourtable
WHERE id > 234374
ORDER BY id
LIMIT 20

"234374" ที่นี่เป็นรหัสของบันทึกล่าสุดจากหน้าก่อนหน้าที่คุณดู

การทำเช่นนี้จะช่วยให้สามารถใช้ดัชนีบน ID เพื่อค้นหาระเบียนแรกได้ หากคุณใช้LIMIT offset, 20คุณจะพบว่ามันช้าลงเรื่อย ๆ เมื่อคุณเลื่อนไปจนสุด อย่างที่ฉันพูดไปมันอาจจะไม่สำคัญว่าถ้าคุณมีเพียง 200 บันทึก แต่มันสามารถสร้างความแตกต่างกับชุดผลลัพธ์ที่ใหญ่กว่าได้

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

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


1
มันเป็นแบบนี้มากกว่า: P ในขณะที่ฉันไม่เห็นด้วยกับการบอกนัย ๆ ว่ารหัส 'รุ่นใหม่' นั้นใหญ่กว่าเสมอกว่า 'รุ่นเก่า' ส่วนใหญ่เวลานี้จะเป็นจริงและฉันคิดว่านี่เป็น 'ดี พอ'. อย่างไรก็ตามใช่ดังที่คุณแสดงให้เห็นว่าการให้เลขหน้าอย่างเหมาะสม (โดยไม่มีการลดลงของประสิทธิภาพอย่างรุนแรงในชุดผลลัพธ์ขนาดใหญ่) ไม่ใช่เรื่องเล็กน้อยและเขียนโดยเฉพาะlimit 1000000, 10และหวังว่ามันจะได้ผล
shylent

1
การเชื่อมโยงการค้นหาปลายเป็นประโยชน์มาก
pvgoddijn

1
เลขหน้านี้ทำงานย้อนหลังหากคุณเพียงแค่ใช้ "DESC" สำหรับการสั่งซื้อ id ฉันชอบมัน!
Dennis Heiden

2
แต่บ่อยครั้งที่ผู้คนต้องการสั่งซื้อด้วย ID หรือโดยการแทรกซึมตาม "วันที่สร้าง" ในโลกแห่งความจริง
RichieHH

โพสต์ดี แต่area=width*heightดังนั้นมันจึงไม่ได้เป็นเพียงปริมาณของระเบียนที่อาจจะสำคัญ แต่ขนาดของแต่ละบันทึกยังเป็นปัจจัยเมื่อเก็บผลในหน่วยความจำ
nothingisnecessary

43

กำหนดOFFSETสำหรับแบบสอบถาม ตัวอย่างเช่น

หน้า 1 - (บันทึก 01-10): offset = 0, limit = 10;

หน้า 2 - (บันทึก 11-20) offset = 10, limit = 10;

และใช้แบบสอบถามต่อไปนี้:

SELECT column FROM table LIMIT {someLimit} OFFSET {someOffset};

ตัวอย่างสำหรับหน้า 2:

SELECT column FROM table
LIMIT 10 OFFSET 10;

1
คุณไม่ได้หมายถึง offset = 10 สำหรับหน้า -2 ใช่หรือไม่
Jenna Maiz

28

มีวรรณกรรมเกี่ยวกับเรื่องนี้:

ปัญหาหลักที่เกิดขึ้นกับการใช้งานของที่มีขนาดใหญ่OFFSETs พวกเขาหลีกเลี่ยงการใช้OFFSETกับเทคนิคที่หลากหลายตั้งแต่การidเลือกช่วงในWHEREข้อไปจนถึงการแคชหรือการคำนวณล่วงหน้า

มีวิธีแก้ไขปัญหาที่แนะนำที่ใช้ INDEX ลุค :


1
getiing max id สำหรับแต่ละการสอบถามเพจจิ้งของการค้นหาที่ซับซ้อนจะส่งผลให้เกิดการใช้งานที่ไม่ได้ผล, การใช้งานที่ไม่เกี่ยวกับการผลิตไม่ได้อันดับ, หมายเลขแถวและระหว่างประเภทส่วนคำสั่งของเพจจิ้ง
Rizwan Patel

กลยุทธ์นั้นจะถูกนำมาพิจารณาและประเมินผลอย่างเหมาะสมในลิงก์ที่ให้ไว้ มันไม่ง่ายเลย
Luchostein

ลิงก์ที่ให้ไว้ดูเหมือนว่าจะเติม pivot uni-pivot พื้นฐาน, ใช้ข้าม, หลาย CTE หรือกลไกตารางที่ได้รับ? อีกครั้งฉันยืนตามกรณีของฉันด้วยคำสั่งเขียนใหม่ในขนาดดังกล่าวอีกครั้งเพื่อรับ maxid เป็นสถาปัตยกรรมมากเกินไป! จากนั้นการเรียงสับเปลี่ยนและการรวมกันอีกครั้งสำหรับ n "จำนวนคอลัมน์ที่มีคำสั่งจัดเรียง!
Rizwan Patel

1
ฉันเข้าใจผิดหรือไม่ว่าลิงก์ "การแบ่งหน้าแบบถูกต้อง" หรือเป็นเพียงการทำสิ่งใด ๆ ที่เกี่ยวข้องกับการกรอง
contactmatt

1
@contactmatt ฉันแบ่งปันความเข้าใจของคุณ ในท้ายที่สุดดูเหมือนว่าไม่มีวิธีที่จะใช้ความต้องการอย่างเต็มประสิทธิภาพ แต่มีการเปลี่ยนแปลงที่ผ่อนคลายรอบ ๆ ต้นฉบับ
Luchostein

13

บทช่วยสอนนี้แสดงวิธียอดเยี่ยมในการให้เลขหน้า การแบ่งหน้าอย่างมีประสิทธิภาพโดยใช้ MySQL

ในระยะสั้นหลีกเลี่ยงการใช้ OFFSET หรือขีด จำกัด ขนาดใหญ่


24
อาจจะให้สรุปหรือไม่?
แอนดรู

ใช่ฉันจะขอบคุณความพยายามมากขึ้นในคำตอบ
Zorkind

6

คุณสามารถทำได้

SELECT SQL_CALC_FOUND_ROWS * FROM tbl limit 0, 20

จำนวนแถวของคำสั่ง select (ไม่ จำกัด ) จะถูกบันทึกในคำสั่ง select เดียวกันเพื่อให้คุณไม่จำเป็นต้องค้นหาขนาดตารางอีกครั้ง คุณได้รับจำนวนแถวโดยใช้ SELECT FOUND_ROWS ();


1
สิ่งนี้ไม่มีประสิทธิภาพอย่างยิ่ง *ผลลัพธ์ในคอลัมน์มากกว่าเป็นสิ่งจำเป็นที่เรียกและSQL_CALC_FOUND_ROWSผลลัพธ์ในคอลัมน์ดังกล่าวถูกอ่านจากทุกแถวในตารางถึงแม้ว่าพวกเขาจะไม่ได้รวมอยู่ในผล มันจะมีประสิทธิภาพมากขึ้นในการคำนวณจำนวนแถวในแบบสอบถามแยกต่างหากซึ่งไม่ได้อ่านคอลัมน์เหล่านั้นทั้งหมด จากนั้นแบบสอบถามหลักของคุณสามารถหยุดหลังจากอ่าน 20 แถว
thomasrutter

คุณแน่ใจไหม? ฉันหมดเวลาสอบถามกับตารางขนาดใหญ่ SQL_CALC_FOUND_ROWS และแบบสอบถามอื่นไม่ได้ใช้ ฉันไม่เห็นความแตกต่างเวลา วิธีใดก็ได้ที่เร็วกว่าการสืบค้น 2 แบบสอบถาม 1 - เลือก * จากขีด จำกัด atable 0 20 แล้วเลือกนับ (*) จากสามารถกำหนดได้
surajz

1
ใช่ฉันแน่ใจว่า - นี่คือข้อมูลเพิ่มเติม ในทุกกรณีเมื่อคุณใช้ดัชนีเพื่อกรองแถว SQL_CALC_FOUND_ROWS จะช้ากว่าการสืบค้น 2 ครั้ง ในโอกาสที่หายากที่คุณไม่ได้ใช้ดัชนีหรือ (ตามตัวอย่างที่ง่ายนี้) คุณไม่มี WHERE clause และเป็นตาราง MYISAM มันสร้างความแตกต่างเล็กน้อย (อยู่ในความเร็วเดียวกัน)
thomasrutter


4

แบบสอบถาม 1: SELECT * FROM yourtable WHERE id > 0 ORDER BY id LIMIT 500

แบบสอบถาม 2: SELECT * FROM tbl LIMIT 0,500;

การสืบค้น 1 ทำงานเร็วขึ้นด้วยระเบียนขนาดเล็กหรือขนาดกลางถ้าจำนวนระเบียนเท่ากับ 5,000 หรือสูงกว่าผลลัพธ์จะคล้ายกัน

ผลลัพธ์ 500 รายการ:

Query1 ใช้เวลา 9.9999904632568 มิลลิวินาที

Query2 ใช้เวลา 19.999980926514 มิลลิวินาที

ผลลัพธ์ 8,000 รายการ:

Query1 ใช้เวลา 129.99987602234 มิลลิวินาที

Query2 ใช้เวลา 160.00008583069 มิลลิวินาที


idคุณจะต้องใส่ดัชนีบน
Maarten

6
มีid > 0ประโยชน์อย่างไร
Michel Jung

1
อย่างที่มาร์ตินกล่าวว่าข้อความค้นหาทั้งสองนั้นปรากฏโดยพื้นฐานเหมือนกันและอาจแยกย่อยเป็นคำสั่งระดับเดียวกันทั้งสองวิธี คุณต้องมีปัญหาการจัดทำดัชนีหรือ MySQL รุ่นเก่าจริง ๆ
HoldOffHunger

ขอบคุณเช่นเดียวกับที่ฉันไม่เห็นคำตอบของคุณฉันแค่ต้องเห็นคำสั่งที่ซึ่งการสั่งซื้อและการ จำกัด มา
Shreyan Mehta

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

0

การเพจนั้นง่ายเมื่อดึงข้อมูลจากตารางเดียว แต่จะซับซ้อนเมื่อดึงข้อมูลที่เข้าร่วมหลายตาราง นี่คือตัวอย่างที่ดีของ MySql และ Spring:
https://www.easycodeforall.com/zpagination1.jsp


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