Dữ liệu MySQL - Cách tốt nhất để thực hiện phân trang?


208

Ứng dụng iPhone của tôi kết nối với dịch vụ web PHP của tôi để lấy dữ liệu từ cơ sở dữ liệu MySQL. Một yêu cầu có thể trả về 500 kết quả.

Cách tốt nhất để thực hiện phân trang và lấy 20 mục cùng một lúc là gì?

Giả sử tôi nhận được 20 quảng cáo đầu tiên từ cơ sở dữ liệu của mình. Bây giờ làm thế nào tôi có thể yêu cầu cho 20 quảng cáo tiếp theo?

Câu trả lời:


309

Từ tài liệu MySQL :

Mệnh đề LIMIT có thể được sử dụng để hạn chế số lượng hàng được trả về bởi câu lệnh SELECT. LIMIT lấy một hoặc hai đối số số, cả hai phải là hằng số nguyên không âm (trừ khi sử dụng các câu lệnh đã chuẩn bị).

Với hai đối số, đối số thứ nhất chỉ định phần bù của hàng đầu tiên sẽ trả về và đối số thứ hai chỉ định số lượng hàng tối đa sẽ trả về. Độ lệch của hàng ban đầu là 0 (không phải 1):

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

Để truy xuất tất cả các hàng từ một độ lệch nhất định cho đến cuối tập kết quả, bạn có thể sử dụng một số lượng lớn cho tham số thứ hai. Câu lệnh này lấy tất cả các hàng từ hàng thứ 96 đến hàng cuối cùng:

SELECT * FROM tbl LIMIT 95,18446744073709551615;

Với một đối số, giá trị chỉ định số lượng hàng sẽ trả về từ đầu tập kết quả:

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

Nói cách khác, LIMIT row_count tương đương với LIMIT 0, row_count.


107
Khi sử dụng LIMIT để phân trang, bạn cũng nên chỉ định ĐẶT HÀNG B .NG.
Mark Byers

10
@shylent: Không có gì sai khi trích dẫn tài liệu, nhưng tôi đồng ý rằng anh ta nên đề cập rằng anh ta đã sao chép các tài liệu và cung cấp một liên kết đến nguồn gốc. Ngoài ra, tôi cũng ngạc nhiên rằng tài liệu này sẽ bao gồm các ví dụ về việc sử dụng GIỚI HẠN mà không cần ĐẶT HÀNG B ... NG ... có vẻ như đó là một cách thực hành tồi để khuyến khích. Nếu không có ĐẶT HÀNG B BYNG, không có gì đảm bảo rằng đơn hàng sẽ giống nhau giữa các cuộc gọi.
Mark Byers

13
Dù sao, khi phân trang các kết quả lớn (và đó là phân trang dành cho - chia các kết quả lớn thành các phần nhỏ hơn, phải không?), bạn nên nhớ rằng nếu bạn thực hiện limit X, Y, điều cơ bản xảy ra là các hàng X + Y được lấy ra và sau đó X hàng từ đầu được bỏ và bất cứ điều gì còn lại được trả lại. Để nhắc lại: limit X, Ykết quả quét các hàng X + Y.
shylent

7
Tôi không thích ý tưởng LIMIT 95, 18446744073709551615 của bạn .. hãy xem OFFSET;-)
CharlesLeaf

5
Điều này không hiệu quả khi làm việc với dữ liệu lớn. Kiểm tra codular.com/im vâying-pagination để biết cách thức đa dạng whicg phù hợp với scenerio cụ thể.
Amit

124

Đối với 500 bản ghi hiệu quả có thể không phải là một vấn đề, nhưng nếu bạn có hàng triệu bản ghi thì có thể thuận lợi khi sử dụng mệnh đề WHERE để chọn trang tiếp theo:

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

"233374" ở đây là id của bản ghi cuối cùng từ trang thịnh hành mà bạn đã xem.

Điều này sẽ cho phép một chỉ mục trên id được sử dụng để tìm bản ghi đầu tiên. Nếu bạn sử dụng, LIMIT offset, 20bạn có thể thấy rằng nó ngày càng chậm hơn khi trang của bạn đến cuối. Như tôi đã nói, có lẽ sẽ không có vấn đề gì nếu bạn chỉ có 200 hồ sơ, nhưng nó có thể tạo ra sự khác biệt với các tập kết quả lớn hơn.

Một ưu điểm khác của phương pháp này là nếu dữ liệu thay đổi giữa các cuộc gọi bạn sẽ không bỏ lỡ các bản ghi hoặc nhận được một bản ghi lặp lại. Điều này là do việc thêm hoặc xóa một hàng có nghĩa là phần bù của tất cả các hàng sau khi nó thay đổi. Trong trường hợp của bạn, điều đó có thể không quan trọng - tôi đoán nhóm quảng cáo của bạn không thay đổi quá thường xuyên và dù sao thì không ai sẽ nhận ra nếu họ nhận được cùng một quảng cáo hai lần liên tiếp - nhưng nếu bạn đang tìm kiếm "cách tốt nhất" thì đây là một điều cần lưu ý khi lựa chọn sử dụng phương pháp nào.

Nếu bạn muốn sử dụng LIMIT với phần bù (và điều này là cần thiết nếu người dùng điều hướng trực tiếp đến trang 10000 thay vì phân trang qua từng trang một) thì bạn có thể đọc bài viết này về tra cứu hàng muộn để cải thiện hiệu suất của LIMIT với số lượng lớn bù lại.


1
Nó giống như thế này: P Mặc dù tôi hoàn toàn không tán thành hàm ý đó, nhưng id 'mới hơn' luôn lớn hơn so với 'cũ', hầu hết thời gian này thực sự sẽ là trường hợp và vì vậy, tôi nghĩ, điều này là 'tốt đủ'. Dù sao, vâng, như bạn đã chứng minh, phân trang thích hợp (không suy giảm hiệu suất nghiêm trọng trên các kết quả lớn) không đặc biệt tầm thường và bằng văn bản limit 1000000, 10và hy vọng rằng nó sẽ hoạt động sẽ không đưa bạn đến đâu.
shylent

1
liên kết tra cứu muộn rất hữu ích
pvgoddijn

1
Phân trang này hoạt động ngược nếu bạn chỉ sử dụng "DESC" để đặt hàng id. Tôi thích nó!
Dennis Heiden

2
nhưng tần suất mọi người muốn đặt hàng bằng ID hoặc, bằng cách ẩn ý, ​​theo "ngày tạo" trong thế giới thực?
RichieHH

bài đăng tốt, nhưng area=width*heightvì vậy, nó không chỉ là số lượng hồ sơ có thể quan trọng, mà kích thước của mỗi bản ghi cũng là một yếu tố khi lưu trữ kết quả trong bộ nhớ
gì cần thiết vào

43

Xác định OFFSET cho truy vấn. Ví dụ

trang 1 - (hồ sơ 01-10): offset = 0, giới hạn = 10;

trang 2 - (bản ghi 11-20) offset = 10, giới hạn = 10;

và sử dụng truy vấn sau:

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

ví dụ cho trang 2:

SELECT column FROM table
LIMIT 10 OFFSET 10;

1
Ý bạn không phải là offset = 10 cho trang 2?
Jenna Maiz

28

Có tài liệu về nó:

Vấn đề chính xảy ra với việc sử dụng OFFSETs lớn . Họ tránh sử dụng OFFSETvới nhiều kỹ thuật khác nhau, từ idlựa chọn phạm vi trong WHEREmệnh đề, đến một số loại trang bộ nhớ đệm hoặc tiền điện toán.

Có các giải pháp được đề xuất tại Sử dụng INDEX, Luke :


1
lấy id tối đa cho mỗi truy vấn phân trang của các truy vấn phức tạp sẽ dẫn đến việc sử dụng không thực tế, không sản xuất có thứ hạng, số hàng và giữa loại mệnh đề phân trang giúp thực hiện!
Rizwan Patel

Chiến lược đó được xem xét và đánh giá đúng trong các liên kết được cung cấp. Nó không đơn giản chút nào.
Luchostein

liên kết được cung cấp dường như chỉ đáp ứng cơ sở đơn trục, áp dụng chéo, đa CTE hoặc cơ chế bảng dẫn xuất? một lần nữa tôi đứng trước trường hợp của mình với việc viết lại các truy vấn ở mức độ lớn như vậy một lần nữa để có được maxid là kiến ​​trúc quá mức cần thiết! và sau đó lại hoán vị và kết hợp cho n "số cột với các đơn hàng sắp xếp!
Rizwan Patel

1
Tôi có hiểu nhầm rằng liên kết "Phân trang được thực hiện đúng cách" hay đơn giản là nó không thực tế trong bất kỳ truy vấn nào liên quan đến việc lọc.
contactmatt

1
@contactmatt Tôi chia sẻ sự hiểu biết của bạn. Cuối cùng, dường như không có cách nào để thực hiện hiệu quả yêu cầu đầy đủ, nhưng thoải mái thay đổi xung quanh bản gốc.
Luchostein


6

bạn cũng có thể làm

SELECT SQL_CALC_FOUND_ROWS * FROM tbl limit 0, 20

Số lượng hàng của câu lệnh select (không có giới hạn) được ghi lại trong cùng một câu lệnh select để bạn không cần phải truy vấn lại kích thước bảng. Bạn nhận được số hàng bằng cách sử dụng SELECT FOUND_lawS ();


1
Điều này đặc biệt không hiệu quả. Các *kết quả trong nhiều cột hơn mức cần thiết được tìm nạp và SQL_CALC_FOUND_ROWSkết quả trong các cột đó được đọc từ tất cả các hàng trong bảng, mặc dù chúng không được bao gồm trong kết quả. Sẽ hiệu quả hơn rất nhiều khi tính toán số lượng hàng trong một truy vấn riêng mà không đọc tất cả các cột đó. Sau đó, truy vấn chính của bạn có thể dừng lại sau khi đọc 20 hàng.
thomasrutter

Bạn có chắc không? Tôi đã hẹn giờ truy vấn đối với một bảng lớn SQL_CALC_FOUND_lawS và một truy vấn khác không sử dụng. Tôi thấy không có sự khác biệt thời gian. Bất kỳ cách nào nó nhanh hơn làm 2 truy vấn. 1 - chọn * từ giới hạn có thể có 0 20, sau đó chọn đếm (*) từ mức có thể.
surajz

1
Có, tôi chắc chắn - đây là thông tin thêm . Trong tất cả các trường hợp khi bạn đang sử dụng một chỉ mục để lọc các hàng, SQL_CALC_FOUND_lawS chậm hơn đáng kể so với thực hiện 2 truy vấn riêng biệt. Trong trường hợp hiếm hoi bạn không sử dụng chỉ mục hoặc (như trong ví dụ đơn giản này), bạn không có mệnh đề WHERE và đó là bảng MYISAM, nó có rất ít sự khác biệt (xung quanh cùng tốc độ).
thomasrutter


4

Truy vấn 1: SELECT * FROM yourtable WHERE id > 0 ORDER BY id LIMIT 500

Truy vấn 2: SELECT * FROM tbl LIMIT 0,500;

Truy vấn 1 chạy nhanh hơn với các bản ghi nhỏ hoặc trung bình, nếu số lượng bản ghi bằng 5.000 hoặc cao hơn, kết quả tương tự.

Kết quả cho 500 hồ sơ:

Truy vấn1 mất 9,9999904632568 mili giây

Truy vấn2 mất 19.999980926514 mili giây

Kết quả cho 8.000 hồ sơ:

Truy vấn1 mất 129.99987602234 mili giây

Truy vấn2 mất 160.00008583069 mili giây


Bạn cần đặt một chỉ số trên id.
Maarten

6
Làm thế nào là id > 0hữu ích?
Michel Jung

1
Giống như Maarten đã nói, hai truy vấn đó về cơ bản giống nhau và có thể được chia thành các lệnh cấp độ máy giống nhau. Bạn phải có một vấn đề về lập chỉ mục hoặc một phiên bản MySQL thực sự cũ.
Hold OfferHunger

cảm ơn, như tôi đã không thấy câu trả lời của bạn, tôi chỉ cần xem thứ tự mà ở đó, thứ tự và giới hạn đến
Shreyan Mehta

ví dụ sai đã được sử dụng. với offset(đối số đầu tiên để giới hạn là offset), bạn vẫn đang chọn tất cả dữ liệu đến giới hạn, sau đó loại bỏ lượng bù đó, sau đó trả về phần nằm giữa offsetlimit. với wheremệnh đề mặt khác, bạn đang thiết lập một loại điểm bắt đầu cho truy vấn và truy vấn ONLYphần cụ thể đó.
senaps

0

Phân trang đơn giản khi nó tìm nạp dữ liệu từ một bảng duy nhất nhưng nó phức tạp khi truy xuất dữ liệu nối nhiều bảng. Dưới đây là một ví dụ hay với MySql và Spring:
https://www.easycodeforall.com/zpagination1.jsp


Vui lòng không chia sẻ liên kết đến các trang web bên thứ ba mà một ngày nào đó có thể biến mất. Nếu bạn đang tìm cách trả lời câu hỏi của tác giả, hãy đăng mã có liên quan để hỗ trợ họ.
Không có thương hiệu Manchester
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.