Domanda

So che è generalmente una cattiva idea fare domande come questa:

SELECT * FROM `group_relations`

Ma quando voglio solo il conteggio, dovrei cercare questa query poiché ciò consente alla tabella di cambiare ma produce comunque gli stessi risultati.

SELECT COUNT(*) FROM `group_relations`

O più specifico

SELECT COUNT(`group_id`) FROM `group_relations`

Ho la sensazione che quest'ultimo potrebbe essere potenzialmente più veloce, ma ci sono altre cose da considerare?

Aggiorna : sto usando InnoDB in questo caso, mi dispiace per non essere più specifico.

È stato utile?

Soluzione

Se la colonna in questione NON è NULL, entrambe le query sono equivalenti. Quando group_id contiene valori null,

select count(*)

conterà tutte le righe, mentre

select count(group_id)

conterà solo le righe in cui group_id non è null.

Inoltre, alcuni sistemi di database, come MySQL, impiegano un'ottimizzazione quando si richiede conteggio (*) che rende tali query un po 'più veloci di quella specifica.

Personalmente, quando conto solo, sto facendo count (*) per essere al sicuro con i null.

Altri suggerimenti

Se ricordo bene, in MYSQL COUNT (*) conta tutte le righe, mentre COUNT (nome_colonna) conta solo le righe che hanno un valore non NULL nella colonna specificata.

COUNT (*) conta tutte le righe mentre COUNT (nome_colonna) conterà solo le righe senza valori NULL nella colonna specificata.

Importante da notare in MySQL:

COUNT () è molto veloce nelle tabelle MyISAM per colonne * o non null, poiché il conteggio delle righe viene memorizzato nella cache. InnoDB non ha cache di conteggio delle righe, quindi non vi è alcuna differenza nelle prestazioni di COUNT (*) o COUNT (nome_colonna), indipendentemente dal fatto che la colonna possa essere nulla o meno. Puoi leggere ulteriori informazioni sulle differenze su questo post all'indirizzo Blog sulle prestazioni di MySQL.

se provi SELECT COUNT (1) DA group_relations sarà un po 'più veloce perché non proverà a recuperare informazioni dalle tue colonne.

Modifica: ho appena fatto qualche ricerca e ho scoperto che ciò accade solo in alcuni db. In sqlserver è lo stesso usare 1 o *, ma su Oracle è più veloce usare 1.

http: //social.msdn.microsoft.com/forums/en-US/transactsql/thread/9367c580-087a-4fc1-bf88-91a51a4ee018/

Apparentemente non c'è alcuna differenza tra loro in mysql, come sqlserver il parser sembra cambiare la query per selezionare (1). Scusa se ti ho fuorviato in qualche modo.

Ero curioso di questo da solo. Va bene leggere la documentazione e le risposte teoriche, ma mi piace bilanciare quelli con prove empiriche.

Ho una tabella MySQL (InnoDB) che contiene 5.607.997 record. La tabella è nella mia sandbox privata, quindi so che i contenuti sono statici e nessun altro sta usando il server. Penso che questo rimuova efficacemente tutti gli effetti esterni sulle prestazioni. Ho una tabella con un campo Chiave primaria (ID) auto_increment che so che non sarà mai nullo che userò per il mio test della clausola where (ID WHERE NON È NULL).

L'unica altra possibile anomalia che vedo nell'esecuzione dei test è la cache. La prima volta che viene eseguita una query sarà sempre più lento delle query successive che utilizzano gli stessi indici. Mi riferirò a quello sotto come la chiamata di seeding della cache. Solo per mescolarlo un po 'l'ho eseguito con una clausola where so che valuterò sempre true indipendentemente da qualsiasi dato (TRUE = TRUE).

Detto questo sono i miei risultati:

Tipo di query

      |  w/o WHERE          | where id is not null |  where true=true

COUNT ()

      |  9 min 30.13 sec ++ | 6 min 16.68 sec ++   | 2 min 21.80 sec ++
      |  6 min 13.34 sec    | 1 min 36.02 sec      | 2 min 0.11 sec 
      |  6 min 10.06 se     | 1 min 33.47 sec      | 1 min 50.54 sec

COUNT (Id)

      |  5 min 59.87 sec    | 1 min 34.47 sec      | 2 min 3.96 sec 
      |  5 min 44.95 sec    | 1 min 13.09 sec      | 2 min 6.48 sec

COUNT (1)

      | 6 min 49.64 sec    | 2 min 0.80 sec       | 2 min 11.64 sec
      | 6 min 31.64 sec    | 1 min 41.19 sec      | 1 min 43.51 sec

++ Questa è considerata la chiamata Seeding della cache. Si prevede che sarà più lento del resto.

Direi che i risultati parlano da soli. COUNT (ID) di solito segna gli altri. L'aggiunta di una clausola Where riduce drasticamente il tempo di accesso anche se è una clausola che sai valuterà come vera. Il punto debole sembra essere COUNT (ID) ... DOVE ID NON È NULL.

Mi piacerebbe vedere i risultati di altre persone, magari con tabelle più piccole o con clausole su campi diversi rispetto al campo che stai contando. Sono sicuro che ci sono altre varianti che non ho preso in considerazione.

Cerca alternative

Come hai visto, quando le tabelle diventano grandi, le query COUNT diventano lente. Penso che la cosa più importante sia considerare la natura del problema che stai cercando di risolvere. Ad esempio, molti sviluppatori utilizzano query COUNT quando generano impaginazione per grandi set di record al fine di determinare il numero totale di pagine nel set di risultati.

Sapendo che le query COUNT diventeranno lente, potresti prendere in considerazione un modo alternativo per visualizzare i controlli di impaginazione che ti consente semplicemente di eseguire il side-step della query lenta. L'impaginazione di Google è un ottimo esempio.

denormalizzare

Se devi assolutamente conoscere il numero di record corrispondenti a un conteggio specifico, considera la tecnica classica di denormalizzazione dei dati. Invece di contare il numero di righe al momento della ricerca, prendere in considerazione l'incremento di un contatore all'inserimento dei record e il decremento di tale contatore alla cancellazione dei record.

Se decidi di farlo, considera di utilizzare operazioni transazionali idempotenti per mantenere sincronizzati quei valori denormalizzati.

BEGIN TRANSACTION;
INSERT INTO  `group_relations` (`group_id`) VALUES (1);
UPDATE `group_relations_count` SET `count` = `count` + 1;
COMMIT;

In alternativa, è possibile utilizzare i trigger di database se RDBMS li supporta.

A seconda dell'architettura, potrebbe essere sensato utilizzare un livello di memorizzazione nella cache come memcached per archiviare, incrementare e decrementare il valore denormalizzato e passare semplicemente alla lenta query COUNT quando manca la chiave della cache. Ciò può ridurre la contesa in scrittura complessiva se si dispone di dati molto volatili, sebbene in casi come questo, si dovrà considerare soluzioni all'effetto catasta .

Le tabelle ISAM di MySQL dovrebbero avere l'ottimizzazione per COUNT (*), saltando la scansione della tabella completa.

Un asterisco in COUNT non ha alcun rilevamento con l'asterisco per la selezione di tutti i campi della tabella. È pura spazzatura dire che COUNT (*) è più lento di COUNT (campo)

Intendo che selezionare COUNT (*) è più veloce di selezionare COUNT (campo). Se l'RDBMS ha rilevato che si specifica " * " su COUNT anziché sul campo, non è necessario valutare nulla per incrementare il conteggio. Considerando che se si specifica il campo su COUNT, RDBMS valuterà sempre se il campo è nullo o non contarlo.

Ma se il tuo campo è nullable, specifica il campo in COUNT.

COUNT (*) fatti e miti:

MITO : " InnoDB non gestisce bene le query di conteggio (*) " ;:

La maggior parte delle query di conteggio (*) vengono eseguite allo stesso modo da tutti i motori di archiviazione se si dispone di una clausola WHERE, altrimenti InnoDB dovrà eseguire una scansione completa della tabella.

FATTO : InnoDB non ottimizza le query di conteggio (*) senza la clausola where

È meglio contare da una colonna indicizzata come una chiave primaria.

SELECT COUNT(`group_id`) FROM `group_relations`

Dovrebbe dipendere da ciò che stai effettivamente cercando di ottenere, come ha già detto Sebastian, ovvero chiarire le tue intenzioni! Se stai stai solo contando le righe, scegli COUNT (*) o contando una singola colonna per COUNT (colonna).

Potrebbe valere la pena di consultare anche il tuo fornitore di database. Quando usavo Informix, aveva un'ottimizzazione per COUNT (*) che aveva un costo di esecuzione del piano di query di 1 rispetto al conteggio delle colonne singole o multiple che si tradurrebbe in una cifra più elevata

  

se provi SELEZIONA COUNT (1) DA group_relations sarà un po 'più veloce perché non proverà a recuperare informazioni dalle tue colonne.

COUNT (1) era più veloce di COUNT (*), ma non è più vero, dal momento che i moderni DBMS sono abbastanza intelligenti da sapere che non vuoi sapere delle colonne

Il consiglio che ho ricevuto da MySQL su cose come questa è che, in generale, cercare di ottimizzare una query basata su trucchi come questo può essere una maledizione a lungo termine. Ci sono esempi sulla storia di MySQL in cui la tecnica ad alte prestazioni di qualcuno che si basa su come funziona l'ottimizzatore finisce per essere il collo di bottiglia nella prossima versione.

Scrivi la query che risponde alla domanda che stai ponendo: se vuoi un conteggio di tutte le righe, usa COUNT (*). Se si desidera un conteggio di colonne non nulle, utilizzare COUNT (col) DOVE col NON È NULL. Indicizza in modo appropriato e lascia l'ottimizzazione all'ottimizzatore. Cercare di rendere le tue ottimizzazioni a livello di query a volte può rendere meno efficace l'ottimizzatore incorporato.

Detto questo, ci sono cose che puoi fare in una query per rendere più semplice l'acceleratore, ma non credo che COUNT sia uno di questi.

Modifica: le statistiche nella risposta sopra sono interessanti, comunque. In questo caso, non sono sicuro che ci sia effettivamente qualcosa al lavoro nell'ottimizzatore. Sto solo parlando di ottimizzazioni a livello di query in generale.

  

So che è generalmente una cattiva idea da fare   query come questa:

SELECT * FROM `group_relations`
     

Ma quando voglio solo il conteggio, dovrei   Vado per questa domanda poiché ciò consente   la tabella cambia, ma cede ancora   gli stessi risultati.

SELECT COUNT(*) FROM `group_relations`

Come suggerisce la tua domanda, il motivo per cui SELECT * è sconsigliato è che le modifiche alla tabella potrebbero richiedere cambiamenti nel tuo codice. Ciò non si applica a COUNT (*) . È abbastanza raro desiderare il comportamento specializzato che ti dà SELECT COUNT ('group_id') - in genere vuoi conoscere il numero di record. Ecco a cosa serve COUNT (*) , quindi usalo.

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