Dados MySQL - Melhor maneira de implementar paginação?

209

Meu aplicativo para iPhone se conecta ao meu serviço da Web PHP para recuperar dados de um banco de dados MySQL. Uma solicitação pode retornar 500 resultados.

Qual é a melhor maneira de implementar a paginação e recuperar 20 itens por vez?

Digamos que recebo os 20 primeiros anúncios do meu banco de dados. Agora, como posso solicitar os próximos 20 anúncios?

aryaxt
fonte

Respostas:

310

Na documentação do MySQL :

A cláusula LIMIT pode ser usada para restringir o número de linhas retornadas pela instrução SELECT. LIMIT usa um ou dois argumentos numéricos, que devem ser constantes inteiras não-negativas (exceto ao usar instruções preparadas).

Com dois argumentos, o primeiro argumento especifica o deslocamento da primeira linha a retornar e o segundo especifica o número máximo de linhas a serem retornadas. O deslocamento da linha inicial é 0 (não 1):

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

Para recuperar todas as linhas de um determinado deslocamento até o final do conjunto de resultados, você pode usar um número grande para o segundo parâmetro. Esta instrução recupera todas as linhas da 96a linha até a última:

SELECT * FROM tbl LIMIT 95,18446744073709551615;

Com um argumento, o valor especifica o número de linhas a serem retornadas desde o início do conjunto de resultados:

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

Em outras palavras, LIMIT número_de-linha é equivalente a LIMIT 0, número-de-linha.

Faisal Feroz
fonte
108
Ao usar LIMIT para paginação, você também deve especificar um ORDER BY.
22810 Mark Byers
10
@ shylent: Não há nada errado em citar a documentação, mas concordo que ele deveria ter mencionado que estava copiando os documentos e forneceu um link para a fonte original. Também estou surpreso que a documentação inclua exemplos de uso de LIMIT sem um ORDER BY ... que parece uma prática ruim de ser encorajadora. Sem um ORDER BY, não há garantia de que o pedido será o mesmo entre as chamadas.
Mark Byers
13
de qualquer maneira, ao paginar grandes conjuntos de resultados (e é para isso que serve a paginação - divida grandes conjuntos de resultados em partes menores, certo?), lembre-se de que, se você fizer um limit X, Y, o que basicamente acontece é que as linhas X + Y são recuperadas e, em seguida, X linhas desde o início são descartadas e o que resta é retornado. Para reiterar: limit X, Yresulta em varredura de linhas X + Y.
shylent 26/09/10
7
Eu não gosto de seu limite de 95, 18446744073709551615 ideia .. dar uma olhada OFFSET;-)
CharlesLeaf
5
Isso não é eficiente ao trabalhar com dados grandes. Consulte codular.com/implementing-pagination para obter várias maneiras que são adequadas para cenários específicos.
quer
125

Para 500 registros, a eficiência provavelmente não é um problema, mas se você tiver milhões de registros, pode ser vantajoso usar uma cláusula WHERE para selecionar a próxima página:

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

O "234374" aqui é o ID do último registro da página anterior que você visualizou.

Isso permitirá que um índice no id seja usado para encontrar o primeiro registro. Se você usar, LIMIT offset, 20poderá descobrir que fica cada vez mais lento conforme você avança no final. Como eu disse, provavelmente não importa se você possui apenas 200 registros, mas isso pode fazer a diferença com conjuntos de resultados maiores.

Outra vantagem dessa abordagem é que, se os dados mudarem entre as chamadas, você não perderá os registros nem obterá um registro repetido. Isso ocorre porque adicionar ou remover uma linha significa que o deslocamento de todas as linhas após a alteração. No seu caso, provavelmente não é importante - acho que seu pool de anúncios não muda com muita frequência e, de qualquer maneira, ninguém notaria se eles obtivessem o mesmo anúncio duas vezes seguidas - mas se você estiver procurando o "melhor caminho" isso é outra coisa a ter em mente ao escolher qual abordagem usar.

Se você deseja usar LIMIT com um deslocamento (e isso é necessário se um usuário navega diretamente para a página 10000 em vez de paginar pelas páginas uma por uma), você pode ler este artigo sobre pesquisas de linhas atrasadas para melhorar o desempenho de LIMIT com uma grande Deslocamento.

Mark Byers
fonte
1
É mais ou menos assim: P Embora eu desaprove absolutamente a implicação, que os IDs 'mais recentes' são sempre maiores do que os 'mais antigos', na maioria das vezes esse será realmente o caso e, então, acho que isso é 'bom o suficiente'. De qualquer forma, sim, como você demonstrou, a paginação adequada (sem degradação severa do desempenho em grandes conjuntos de resultados) não é particularmente trivial e escrita, limit 1000000, 10e esperar que funcione não o levará a lugar algum.
shylent 26/09/10
1
o link de pesquisa final é muito útil
pvgoddijn
1
Essa paginação funciona de trás para frente se você apenas usar "DESC" para solicitar o ID. Eu gosto disso!
Dennis Heiden
2
mas com que frequência as pessoas desejam solicitar por ID ou por insinuação, por "data criada" no mundo real?
RichieHH
bom post, mas area=width*heightpor isso não é apenas a quantidade de registros que possam importam, mas o tamanho de cada registro também é um fator quando armazenar os resultados na memória
nothingisnecessary
43

Defina OFFSET para a consulta. Por exemplo

página 1 - (registros 01-10): deslocamento = 0, limite = 10;

página 2 - (registros 11-20) deslocamento = 10, limite = 10;

e use a seguinte consulta:

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

exemplo para a página 2:

SELECT column FROM table
LIMIT 10 OFFSET 10;
Prabodh Hend
fonte
1
Você não quer dizer deslocamento = 10 para a página 2?
Jenna Maiz
28

Há literatura sobre isso:

O principal problema ocorre com o uso de grandes OFFSET s . Eles evitam usar OFFSETvárias técnicas, desde idseleções de intervalo na WHEREcláusula até algum tipo de cache ou pré-computação de páginas.

Existem soluções sugeridas em Use the INDEX, Luke :

Luchostein
fonte
1
A obtenção de um ID máximo para cada consulta de paginação de consultas complexas resultaria em impraticabilidade, o uso sem produção classifica, número de linhas e, entre cláusulas, o tipo de paginação ajuda na performance!
Rizwan Patel
Essa estratégia é levada em consideração e avaliada adequadamente nos links fornecidos. Não é tão simples assim.
Luchostein
o link fornecido parece cumprir apenas o pivô básico, o pivô básico, a aplicação cruzada, a multi CTE ou a mecânica de tabela derivada? novamente, eu mantenho meu caso com a reescrita de consultas em tal magnitude novamente para obter o máximo é um exagero arquitetônico! e depois novamente permutação e combinação de n" número de coluna com ordens de classificação!
Rizwan Patel
1
Estou entendendo mal que o link "Paginação da maneira correta" ou é simplesmente impraticável em qualquer consulta que envolva filtragem.
contactmatt
1
@contactmatt Partilho a sua apreensão. No final, parece que não há como implementar eficientemente todos os requisitos, mas variações relaxadas em torno do original.
Luchostein
13

Este tutorial mostra uma ótima maneira de fazer paginação. Paginação eficiente usando MySQL

Em resumo, evite usar OFFSET ou LIMIT grande

Bao Le
fonte
24
talvez dê um resumo?
Andrew
Sim, eu apreciaria mais esforço na resposta.
Zorkind 17/07/19
6

você também pode fazer

SELECT SQL_CALC_FOUND_ROWS * FROM tbl limit 0, 20

A contagem de linhas da instrução select (sem o limite) é capturada na mesma instrução select, para que você não precise consultar o tamanho da tabela novamente. Você obtém a contagem de linhas usando SELECT FOUND_ROWS ();

surajz
fonte
1
Isso é particularmente ineficiente. Os *resultados em mais colunas do que o necessário estão sendo buscados e os SQL_CALC_FOUND_ROWSresultados nessas colunas são lidos de todas as linhas da tabela, mesmo que não estejam incluídos no resultado. Seria muito mais eficiente calcular o número de linhas em uma consulta separada que não lê todas essas colunas. Em seguida, sua consulta principal pode parar após a leitura de 20 linhas.
thomasrutter
Você tem certeza? Programei a consulta em uma tabela grande SQL_CALC_FOUND_ROWS e outra consulta não estava sendo usada. Não vi diferença horária. De qualquer maneira, é mais rápido do que fazer duas consultas. 1 - selecione * do limite de ativação 0 20 e, em seguida, selecione contagem (*) da ativação.
surajz
1
Sim, tenho certeza - aqui está mais informações . Em todos os casos, quando você estiver usando um índice para filtrar linhas, SQL_CALC_FOUND_ROWS é significativamente mais lento do que fazendo duas consultas separadas. Nas raras ocasiões em que você não está usando um índice, ou (como neste exemplo simplificado), você não possui a cláusula WHERE e é uma tabela MYISAM, faz pouca diferença (está na mesma velocidade).
thomasrutter
Também aqui está uma discussão sobre isso no Stackoverflow
thomasrutter
4

Consulta 1: SELECT * FROM yourtable WHERE id > 0 ORDER BY id LIMIT 500

Consulta 2: SELECT * FROM tbl LIMIT 0,500;

A consulta 1 é executada mais rapidamente com registros pequenos ou médios; se o número de registros for igual a 5.000 ou superior, o resultado será semelhante.

Resultado para 500 registros:

Query1 take 9.9999904632568 milissegundos

Query2 leva 19,999980926514 milissegundos

Resultado para 8.000 registros:

Query1 leva 129.99987602234 milissegundos

Query2 leva 160.00008583069 milissegundos

Huy
fonte
Você precisa colocar um índice id.
Maarten
6
Como é id > 0útil?
Michel Jung
1
Como Maarten disse, essas duas consultas parecem fundamentalmente iguais e, provavelmente, se dividem nos mesmos comandos no nível da máquina. Você deve ter um problema de indexação ou uma versão muito antiga do MySQL.
precisa saber é o seguinte
graças, como em eu não vi sua resposta, eu só precisava ver a ordem em que, onde, ordem e limite vem
Shreyan Mehta
exemplo errado foi usado. com offset(o primeiro argumento a limitar é deslocamento), você ainda está selecionando todos os dados até o limite, descartando a quantidade do deslocamento e retornando a seção que está entre offsete limit. com a wherecláusula, por outro lado, você está definindo um tipo de ponto de partida para a consulta e consulta ONLYa parte específica.
senaps 7/09/19
0

A paginação é simples quando busca dados de uma única tabela, mas é complexa quando recupera dados que unem várias tabelas. Aqui está um bom exemplo com o MySql e o Spring:
https://www.easycodeforall.com/zpagination1.jsp

Susanta Ghosh
fonte
Por favor, não compartilhe links para sites de terceiros que possam um dia desaparecer. Se você deseja responder à pergunta dos autores, publique o código relevante para ajudá-los.
Manchester sem marca