COUNT(*) gegen COUNT(spalte)

Immer wieder benötigt man in SQL-Abfragen die Anzahl der zurückgegebenen Datensätze. Dazu dient die SQL-Funktion COUNT(). Als Argument nimmt diese Funktion entweder ein ‚*‘ oder eine bestimmte Spalte. Im MySQL Performance Blog wird verglichen, ob man dabei COUNT(*) oder COUNT(spalte) verwenden sollte.

COUNT(*) greift auf den internen Cache von MySQL zurück und kann deshalb recht schnell die Anzahl zählen. Das geht allerdings nur, wenn die zu zählende Spalte NOT NULL gesetzt ist, da sonst NULL-Werte auftreten können, die nicht mitgezählt werden dürfen.
MySQL Performance Blog zeigt das mit diesem Code:

CREATE TABLE `fact` (
        `i` INT(10) UNSIGNED NOT NULL,
        `val` INT(11) DEFAULT NULL,
        `val2` INT(10) UNSIGNED NOT NULL,
        KEY `i` (`i`)
      ) ENGINE=MyISAM DEFAULT CHARSET=latin1
 
 
      mysql> SELECT COUNT(*) FROM fact;
      +----------+
      | COUNT(*) |
      +----------+
      |  7340032 |
      +----------+
      1 ROW IN SET (0.00 sec)
 
 
      mysql> SELECT COUNT(val) FROM fact;
      +------------+
      | COUNT(val) |
      +------------+
      |    7216582 |
      +------------+
      1 ROW IN SET (1.17 sec)
 
 
      mysql> SELECT COUNT(val2) FROM fact;
      +-------------+
      | COUNT(val2) |
      +-------------+
      |     7340032 |
      +-------------+
      1 ROW IN SET (0.00 sec)

In die Beispiel-Tabelle wurden zufällige Werte eingetragen, unter anderem auch NULL-Werte in die Spalte val. Und eben weil diese Spalte nicht NOT NULL gesetzt wurde, muss MySQL wirklich durchzählen (und daraus ergibt sich dann auch das unterschiedliche Ergebnis bei der zweiten Query).

Man erkennt daran, dass COUNT(*) schneller ist, manchmal aber COUNT(spalte) trotzdem Sinn macht, nämlich wenn NULL-Werte gewollt nicht mitgezählt werden sollen.

Wenn man an die Query noch eine Bedingung hängt, sind die Unterschiede anders zu begründen:

      mysql> SELECT count(*) FROM fact WHERE i<10000;
      +----------+
      | count(*) |
      +----------+
      |   733444 |
      +----------+
      1 row IN SET (0.40 sec)
 
 
      mysql> SELECT count(val) FROM fact WHERE i<10000;
      +------------+
      | count(val) |
      +------------+
      |     720934 |
      +------------+
      1 row IN SET (1.29 sec)
 
 
      mysql> SELECT count(val2) FROM fact WHERE i<10000;
      +-------------+
      | count(val2) |
      +-------------+
      |      733444 |
      +-------------+
      1 row IN SET (1.30 sec)

Der Grund für den Geschwindigkeitsvorteil für COUNT(*) gegenüber COUNT(val) ist hier, dass COUNT(*) den Index für i benutzen kann, während COUNT(val) das nicht kann. Natürlich könnte man den Index auf (i, val) erweitern, aber das ist eben nicht nötig (es sei denn es handelt sich um eine Fremdanwendung, da würde ich den Index erweitern). Allerdings braucht die Query auch mit dem erweiterten Index und COUNT(val) doppelt so lange wie COUNT(*), was wahrscheinlich daran liegt, dass der Index größer geworden ist.

Merke: Es sollte stets COUNT(*) zum Zählen der zurückgegebenen Datensätze verwendet werden.
Es gibt allerdings 2 Ausnahmen:

  1. Es sollen wirklich nur die NOT-NULL-Werte gezählt werden –> COUNT(NULL_Spalte)
  2. Man hat keinen Zugriff auf den Quellcode der Anwendung (Fremdanwendung) –> Index für optimale Performance erweitern / setzen

Jan hat 152 Beiträge geschrieben

Ein Kommentar zu “COUNT(*) gegen COUNT(spalte)

Eine Antwort schreiben

Ihre E-Mail-Adresse wird nicht veröffentlicht. Benötigte Felder sind markiert mit *

You may use these HTML tags and attributes: <a href=""> <blockquote cite=""> <pre lang=""> <b> <strong> <i> <em>