Pergunta

Quando a paginação através de dados provenientes de um DB, você precisa saber quantas páginas haverá para tornar os controles de página de salto.

Atualmente eu fazer isso executando a consulta duas vezes, uma vez envolvido em um count() para determinar os resultados totais, e uma segunda vez com um limite aplicado para voltar apenas os resultados que eu preciso para a página atual.

Isto parece ineficiente. Existe uma maneira melhor para determinar quantos resultados teriam sido devolvidos antes LIMIT foi aplicado?

Eu estou usando PHP e Postgres.

Foi útil?

Solução

Pure SQL

As coisas mudaram desde 2008. Você pode usar um href="https://www.postgresql.org/docs/current/static/functions-window.html" rel="noreferrer"> função de janela para obter a contagem total e o resultado limitado em uma consulta. (Introduzido com PostgreSQL 8.4 em 2009 ).

SELECT foo
     , count(*) OVER() AS full_count
FROM   bar
WHERE  <some condition>
ORDER  BY <some col>
LIMIT  <pagesize>
OFFSET <offset>

Note que isso pode ser consideravelmente mais caro do que sem a contagem total. Todas as linhas têm de ser contadas, e um possível atalho levando apenas as principais linhas de um índice de correspondência pode não ser útil mais.
Não importa muito com pequenas mesas ou full_count <= OFFSET + LIMIT. Matérias para um full_count substancialmente maior.

caso de canto : quando OFFSET é pelo menos tão grande como o número de linhas a partir da consulta de base, nenhuma linha é retornado. Então você começa também não full_count. Alternativa possível:

Considere o seqüência de eventos :

  1. cláusula WHERE (e condições JOIN, mas não aqui) Filtro de qualificação linhas da tabela base (s).

    (funções GROUP BY e agregados iria aqui.)

  2. funções de janela são aplicados considerando todas as linhas de qualificação (dependendo da cláusula OVER ea especificação quadro da função). O count(*) OVER() simples é baseada em todas as linhas.

  3. ORDER BY

    (DISTINCT ou DISTINCT ON iria aqui.)

  4. LIMIT / OFFSET são aplicadas com base na ordem estabelecida para selecionar linhas de retorno.

LIMIT / OFFSET torna-se cada vez mais ineficiente com um crescente número de linhas na tabela. Considerar abordagens alternativas se você precisar de um melhor desempenho:

Alternativas para obter última contagem

Há completamente diferentes abordagens para obter a contagem de linhas afetadas ( não a contagem completa antes OFFSET & LIMIT foram aplicadas). Postgres tem contabilidade interna quantas linhas onde afetado pelo último comando SQL. Alguns clientes podem acessar essas informações ou contar linhas-se (como psql).

Por exemplo, você pode recuperar o número de linhas afetadas pela plpgsql imediatamente depois de executar um comando SQL com:

GET DIAGNOSTICS integer_var = ROW_COUNT;

detalhes no manual.

Ou você pode usar pg_num_rows em PHP . Ou funções semelhantes em outros clientes.

Relacionado:

Outras dicas

Como eu descrevo no meu blog, MySQL tem um recurso chamado SQL_CALC_FOUND_ROWS . Isso elimina a necessidade de fazer a consulta duas vezes, mas ainda precisa fazer a consulta em sua entireity, mesmo que a cláusula de limite teria permitido para parar cedo.

Tanto quanto eu sei, não há recurso semelhante para o PostgreSQL. Uma coisa que atente para quando fazer a paginação (a coisa mais comum para o qual limite é IMHO utilizado): fazendo um "OFFSET 1000 LIMIT 10" significa que o DB tem para buscar , pelo menos, 1010 linhas, mesmo se ele só lhe dá 10. Uma maneira mais performance que fazer é lembrar o valor da linha que você está requisitando por para a linha anterior (a 1000, neste caso) e reescrever a consulta como esta:" ... ONDE order_row> value_of_1000_th limite de 10" . A vantagem é que "order_row" é provavelmente indexado (se não, você tem ir um problema). A desvantagem é que se novos elementos são adicionados entre as visualizações de página, isso pode ficar um pouco fora de sincronia (mas, novamente, pode não ser observável pelos visitantes e pode ser um ótimo ganho de performance).

Você poderia atenuar a pena de desempenho por não correr o COUNT () consulta o tempo todo. Cache o número de páginas para, digamos, 5 minutos antes da consulta é executado novamente. A menos que você está vendo um grande número de inserções, que deve funcionar muito bem.

Desde Postgres já faz uma certa quantidade de cache coisas, este tipo de método não é tão ineficiente quanto parece. É definitivamente não duplicando o tempo de execução. Temos temporizadores construídos em nossa camada de DB, então eu vi as provas.

Já que você precisa saber para o propósito de paginação, eu sugiro executar a consulta completo uma vez, escrevendo os dados no disco como um cache do lado do servidor, em seguida, a alimentação que através de seu mecanismo de paginação.

Se você estiver executando a consulta COUNT com a finalidade de decidir se a fornecer os dados para o usuário ou não (ou seja, se houver> X registros, devolver um erro), você precisa ficar com a abordagem COUNT.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top