Domanda

Quando si esegue il paging dei dati provenienti da un DB, è necessario sapere quante pagine ci saranno per eseguire il rendering dei controlli di salto della pagina.

Attualmente lo faccio eseguendo la query due volte, una volta racchiuso in un count() per determinare i risultati totali, e una seconda volta con un limite applicato per recuperare solo i risultati di cui ho bisogno per la pagina corrente.

Questo sembra inefficiente. Esiste un modo migliore per determinare quanti risultati sarebbero stati restituiti prima dell'applicazione LIMIT?

Sto usando PHP e Postgres.

È stato utile?

Soluzione

Pure SQL

Le cose sono cambiate dal 2008. Puoi utilizzare una funzione finestra per ottenere il conteggio completo e il risultato limitato in una query. (Introdotto con PostgreSQL 8.4 nel 2009 ).

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

Nota che questo può essere considerevolmente più costoso che senza il conteggio totale. Tutte le righe devono essere contate e una possibile scorciatoia che prende solo le prime righe da un indice corrispondente potrebbe non essere più utile.
Non importa molto con i tavolini o full_count & Lt; = OFFSET + LIMIT. È importante per un WHERE.

sostanzialmente più grande

Custodia ad angolo : quando JOIN è almeno pari al numero di righe della query di base, nessuna riga viene restituito. Quindi non ottieni anche GROUP BY. Alternativa possibile:

Considera la sequenza di eventi :

    La
  1. OVER clausola (e count(*) OVER() condizioni, ma non qui) filtra le righe qualificanti dalle tabelle di base.

    (ORDER BY e le funzioni aggregate andrebbero qui.)

  2. Le funzioni della finestra vengono applicate considerando tutte le righe qualificanti (a seconda della clausola DISTINCT e delle specifiche del frame della funzione). Il semplice DISTINCT ON si basa su tutte le righe.

  3. pg_num_rows

    (<=> o <=> andrebbe qui.)

  4. <=> / <=> vengono applicati in base all'ordine stabilito per selezionare le righe da restituire.

<=> / <=> diventa sempre più inefficiente con un numero crescente di righe nella tabella. Prendi in considerazione approcci alternativi se hai bisogno di prestazioni migliori:

Alternative per ottenere il conteggio finale

Esistono approcci completamente diversi per ottenere il conteggio delle righe interessate ( non il conteggio completo prima dell'applicazione <=> & amp; <=>). Postgres ha una contabilità interna quante righe sono state interessate dall'ultimo comando SQL. Alcuni client possono accedere a tali informazioni o contare le righe stesse (come psql).

Ad esempio, puoi recuperare il numero di righe interessate in plpgsql immediatamente dopo aver eseguito un comando SQL con:

GET DIAGNOSTICS integer_var = ROW_COUNT;

Dettagli nel manuale.

Oppure puoi usare <=> in PHP . O funzioni simili in altri client.

Related:

Altri suggerimenti

Mentre descrivo sul mio blog , MySQL ha una funzione chiamata SQL_CALC_FOUND_ROWS . Ciò elimina la necessità di eseguire la query due volte, ma deve comunque eseguire la query nella sua interezza, anche se la clausola limit avrebbe consentito l'interruzione anticipata.

Per quanto ne so, non esiste una funzione simile per PostgreSQL. Una cosa a cui prestare attenzione quando si esegue l'impaginazione (la cosa più comune per cui viene utilizzato LIMIT IMHO): fare un & Quot; OFFSET 1000 LIMIT 10 & Quot; significa che il DB deve recuperare almeno 1010 righe, anche se ti dà solo 10. Un modo più efficace di fare è ricordare il valore della riga che stai ordinando per la riga precedente ( il 1000 in questo caso) e riscrivere la query in questo modo: " ... WHERE order_row > value_of_1000_th LIMIT 10 " ;. Il vantaggio è che & Quot; order_row & Quot; è molto probabilmente indicizzato (in caso contrario, hai riscontrato un problema). Lo svantaggio è che se si aggiungono nuovi elementi tra le visualizzazioni di pagina, questo può risultare un po 'fuori sincrono (ma, di nuovo, potrebbe non essere osservabile dai visitatori e può essere un grande vantaggio in termini di prestazioni).

È possibile mitigare la penalità delle prestazioni non eseguendo la query COUNT () ogni volta. Cache il numero di pagine per, diciamo 5 minuti prima che la query venga eseguita di nuovo. A meno che tu non stia vedendo un numero enorme di INSERTI, questo dovrebbe funzionare bene.

Dato che Postgres fa già un certo numero di cose nella cache, questo tipo di metodo non è così inefficiente come sembra. Non è sicuramente il raddoppio dei tempi di esecuzione. Abbiamo timer integrati nel nostro livello DB, quindi ho visto le prove.

Visto che è necessario sapere ai fini del paging, suggerirei di eseguire l'intera query una volta, scrivere i dati sul disco come cache sul lato server, quindi alimentarli tramite il meccanismo di paging.

Se stai eseguendo la COUNT query allo scopo di decidere se fornire o meno i dati all'utente (ovvero se ci sono > X record, restituisci un errore), devi attenersi a l'approccio COUNT.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top