Soll ich zählen (*) oder nicht?

72

Ich weiß, dass es im Allgemeinen eine schlechte Idee ist, solche Abfragen durchzuführen:

SELECT * FROM `group_relations`

Aber wenn ich nur die Anzahl haben möchte, sollte ich mich für diese Abfrage entscheiden, da sich dadurch die Tabelle ändern kann, aber immer noch die gleichen Ergebnisse erzielt werden.

SELECT COUNT(*) FROM `group_relations`

Oder umso spezifischer

SELECT COUNT(`group_id`) FROM `group_relations`

Ich habe das Gefühl, dass Letzteres möglicherweise schneller sein könnte, aber gibt es noch andere Dinge zu beachten?

Update : In diesem Fall verwende ich InnoDB. Es tut mir leid, dass ich nicht genauer bin.

grapefrukt
quelle
Sie sollten wirklich angeben, ob Sie über MyISAM oder InnoDB (oder etwas anderes)
sprechen
Gute Frage, darüber habe ich mich auch schon gewundert.
Kip

Antworten:

102

Wenn die betreffende Spalte NICHT NULL ist, sind beide Abfragen gleichwertig. Wenn group_id Nullwerte enthält,

select count(*)

zählt alle Zeilen, während

select count(group_id)

zählt nur die Zeilen, in denen group_id nicht null ist.

Einige Datenbanksysteme wie MySQL verwenden auch eine Optimierung, wenn Sie nach count (*) fragen, wodurch solche Abfragen etwas schneller als die spezifischen sind.

Persönlich, wenn ich nur zähle, zähle ich (*), um mit den Nullen auf der sicheren Seite zu sein.

Pilif
quelle
1
Die Art und Weise, wie MySQL mit der Anzahl umgeht, ist je nach verwendetem Datenbankmodul sehr unterschiedlich. In dieser Antwort wird MyISAM angenommen, aber die Frage lautet INNODB. Ersteres ist schnell und zwischengespeichert, später ist es ziemlich langsam.
Gesindel
2
wahr, aber a) die InnoDB-Notiz war nicht da, als ich die Antwort geschrieben habe und b) während InnoDB die Anzahl nicht optimieren kann, ist count (*) nicht schneller oder langsamer als count (group_id). Und selbst auf MyISAM würden Sie den Geschwindigkeitsvorteil verlieren, sobald Sie eine WHERE-Klausel hinzufügen
Pilif
Auch wenn Sie wissen möchten, ob eine Zeile vorhanden ist, wählen Sie COUNT ( group_id) FROM group_relationsWHERE ROWNUM = 1
Dead Programmer
22

Wenn ich mich richtig erinnere, zählt in MYSQL COUNT (*) alle Zeilen, während COUNT (Spaltenname) nur die Zeilen zählt, die in der angegebenen Spalte einen Wert ungleich NULL haben.

Sebastian Dietz
quelle
11

COUNT (*) zählt alle Zeilen, während COUNT (Spaltenname) nur Zeilen ohne NULL-Werte in der angegebenen Spalte zählt.

Wichtig in MySQL zu beachten:

COUNT () ist in MyISAM-Tabellen für * oder Nicht-Null-Spalten sehr schnell, da die Zeilenanzahl zwischengespeichert wird. InnoDB verfügt über kein Caching der Zeilenanzahl, daher gibt es keinen Leistungsunterschied für COUNT (*) oder COUNT (Spaltenname), unabhängig davon, ob die Spalte null sein kann oder nicht. Weitere Informationen zu den Unterschieden in diesem Beitrag finden Sie im MySQL-Performance-Blog.

Eran Galperin
quelle
1
Vielleicht eine blöde Frage ... Woher weiß ich, ob meine MySQL-Datenbank von Innodb oder Myisam unterstützt wird? Ich laufe auf einem gemeinsam genutzten Server, also habe ich es nicht selbst eingerichtet ...
Kip
1
InnoDB und MyISAM sind Tabellenspeicher-Engines. MySQL verfügt über mehrere davon - dev.mysql.com/doc/refman/5.0/en/storage-engines.html . Ihre Datenbank kann eine Mischung aus Speicher-Engines für verschiedene Tabellen enthalten. Schauen Sie in Ihrem phpMyAdmin nach, um die Speicher-Engine Ihrer verschiedenen Tabellen zu überprüfen
Eran Galperin
8

Wenn Sie SELECT COUNT(1) FROMgroup_relations ausprobieren , ist dies etwas schneller, da nicht versucht wird, Informationen aus Ihren Spalten abzurufen.

Bearbeiten: Ich habe gerade einige Nachforschungen angestellt und herausgefunden, dass dies nur in einigen Datenbanken geschieht. In sqlserver ist es dasselbe, 1 oder * zu verwenden, aber in Oracle ist es schneller, 1 zu verwenden.

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

Anscheinend gibt es keinen Unterschied zwischen ihnen in MySQL, wie bei SQL Server scheint der Parser die Abfrage zu ändern, um (1) auszuwählen. Entschuldigung, wenn ich Sie irgendwie irreführe.

Sergio
quelle
Schneller als COUNT ( )? Warum muss COUNT ( ) Informationen aus den Spalten abrufen?
Kent Boogaart
Wenn MySQL Spalteninformationen für count (*) abruft, ist dies ein sehr guter Grund, dies als DBMS zu vermeiden. Ich kann nicht glauben, dass die Entwickler so dumm gewesen wären.
Paxdiablo
5

Ich war selbst neugierig darauf. Es ist in Ordnung, Dokumentation und theoretische Antworten zu lesen, aber ich mag es, diese mit empirischen Beweisen in Einklang zu bringen.

Ich habe eine MySQL-Tabelle (InnoDB) mit 5.607.997 Datensätzen. Die Tabelle befindet sich in meiner eigenen privaten Sandbox, daher weiß ich, dass der Inhalt statisch ist und niemand den Server verwendet. Ich denke, dies beseitigt effektiv alle äußeren Auswirkungen auf die Leistung. Ich habe eine Tabelle mit einem Auto_increment-Primärschlüsselfeld (ID), von dem ich weiß, dass es niemals null sein wird, das ich für meinen where-Klauseltest verwenden werde (WHERE-ID IST NICHT NULL).

Der einzige andere mögliche Fehler, den ich beim Ausführen von Tests sehe, ist der Cache. Das erste Mal, wenn eine Abfrage ausgeführt wird, ist immer langsamer als nachfolgende Abfragen, die dieselben Indizes verwenden. Ich werde das unten als Cache-Seeding-Aufruf bezeichnen. Um es ein wenig zu verwechseln, habe ich es mit einer where-Klausel ausgeführt, von der ich weiß, dass sie unabhängig von Daten immer als wahr ausgewertet wird (TRUE = TRUE).

Das heißt hier sind meine Ergebnisse:

Abfragetyp

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

ANZAHL()

      |  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

++ Dies wird als Cache-Seeding-Aufruf betrachtet. Es wird erwartet, dass es langsamer als der Rest ist.

Ich würde sagen, die Ergebnisse sprechen für sich. COUNT (Id) verdrängt normalerweise die anderen. Das Hinzufügen einer Where-Klausel verkürzt die Zugriffszeit erheblich, selbst wenn es sich um eine Klausel handelt, von der Sie wissen, dass sie als wahr ausgewertet wird. Der Sweet Spot scheint COUNT (Id) zu sein ... WO Id NICHT NULL ist.

Ich würde gerne die Ergebnisse anderer Leute sehen, vielleicht mit kleineren Tabellen oder mit where-Klauseln gegen andere Felder als das Feld, das Sie zählen. Ich bin sicher, dass es andere Variationen gibt, die ich nicht berücksichtigt habe.

Chris
quelle
Interessanter Test. Ich habe versucht, Ihre Ergebnisse mithilfe einer Tabelle mit über 80 Millionen Datensätzen in MySQL 5.1.45 mit Innodb zu duplizieren. Meine Ergebnisse waren sehr unterschiedlich, count ( ) ohne Where-Klausel war 20% schneller als jede andere Methode. Das Hinzufügen einer Where-Klausel war immer langsamer. Mein Server konnte in 15,2 Sekunden 80 Millionen Zeilen zählen . Ich hatte auch ein vorzeichenloses int ohne Vorzeichen als Primärschlüssel.
Gary
4

Suchen Sie nach Alternativen

Wie Sie gesehen haben, werden COUNTAbfragen langsam , wenn Tabellen groß werden. Ich denke, das Wichtigste ist, die Art des Problems zu berücksichtigen, das Sie lösen möchten. Beispielsweise verwenden viele Entwickler COUNTAbfragen, wenn sie eine Paginierung für große Sätze von Datensätzen generieren, um die Gesamtzahl der Seiten in der Ergebnismenge zu bestimmen.

Da COUNTSie wissen, dass Abfragen langsam werden, können Sie eine alternative Methode zum Anzeigen von Paginierungssteuerelementen in Betracht ziehen, mit der Sie die langsame Abfrage einfach umgehen können. Die Paginierung von Google ist ein hervorragendes Beispiel.

Denormalisieren

Wenn Sie unbedingt die Anzahl der Datensätze kennen müssen, die einer bestimmten Anzahl entsprechen, sollten Sie die klassische Technik der Daten-Denormalisierung in Betracht ziehen. Anstatt die Anzahl der Zeilen zum Zeitpunkt der Suche zu zählen, sollten Sie einen Zähler beim Einfügen eines Datensatzes erhöhen und diesen Zähler beim Löschen des Datensatzes verringern.

Wenn Sie sich dazu entschließen, sollten Sie idempotente Transaktionsoperationen verwenden, um diese denormalisierten Werte synchron zu halten.

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

Alternativ können Sie Datenbank-Trigger verwenden, wenn Ihr RDBMS diese unterstützt.

Abhängig von Ihrer Architektur kann es sinnvoll sein, eine Caching-Ebene wie memcached zum Speichern, Inkrementieren und Dekrementieren des denormalisierten Werts zu verwenden und einfach zur langsamen COUNT-Abfrage zu wechseln, wenn der Cache-Schlüssel fehlt. Dies kann den allgemeinen Schreibkonflikt verringern, wenn Sie über sehr flüchtige Daten verfügen. In solchen Fällen sollten Sie jedoch Lösungen für den Dog-Pile-Effekt in Betracht ziehen .

Duncan Beevers
quelle
2

MySQL ISAM-Tabellen sollten für COUNT (*) optimiert sein und den vollständigen Tabellenscan überspringen.

dmajkic
quelle
2

Ein Sternchen in COUNT hat keine Bedeutung mit einem Sternchen für die Auswahl aller Tabellenfelder. Es ist Unsinn zu sagen, dass COUNT (*) langsamer ist als COUNT (Feld)

Ich denke, dass die Auswahl von COUNT (*) schneller ist als die Auswahl von COUNT (Feld). Wenn das RDBMS feststellt, dass Sie "*" in COUNT anstelle des Felds angeben, muss es nichts auswerten, um die Anzahl zu erhöhen. Wenn Sie dagegen ein Feld in COUNT angeben, bewertet das RDBMS immer, ob Ihr Feld null ist oder nicht, um es zu zählen.

Wenn Ihr Feld jedoch nullwertfähig ist, geben Sie das Feld in COUNT an.

Michael Buen
quelle
2

COUNT (*) Fakten und Mythen:

MYTHOS : "InnoDB verarbeitet Count (*) -Anfragen nicht gut":

Die meisten Zählabfragen (*) werden von allen Speicher-Engines auf dieselbe Weise ausgeführt, wenn Sie eine WHERE-Klausel haben. Andernfalls muss InnoDB einen vollständigen Tabellenscan durchführen.

FAKT : InnoDB optimiert keine Anzahl (*) Abfragen ohne die where-Klausel

Charles Faiga
quelle
2

Es ist am besten, nach einer indizierten Spalte wie einem Primärschlüssel zu zählen.

SELECT COUNT(`group_id`) FROM `group_relations`
pjau
quelle
1

Es sollte davon abhängen, was Sie tatsächlich erreichen wollen, wie Sebastian bereits gesagt hat, dh Ihre Absichten klarstellen! Wenn Sie sind nur die Zeilen zu zählen geht dann für die COUNT (*), oder eine einzelne Spalte geht für den COUNT (Spalte) zu zählen.

Möglicherweise lohnt es sich auch, Ihren DB-Anbieter zu überprüfen. Als ich Informix verwendete, hatte es eine Optimierung für COUNT (*), die Ausführungskosten für einen Abfrageplan von 1 hatte, verglichen mit dem Zählen einzelner oder mehrerer Spalten, was zu einer höheren Zahl führen würde

tddmonkey
quelle
1

Wenn Sie SELECT COUNT (1) FROM group_relations versuchen, ist dies etwas schneller, da nicht versucht wird, Informationen aus Ihren Spalten abzurufen.

COUNT (1) war früher schneller als COUNT (*), aber das stimmt nicht mehr, da moderne DBMS klug genug sind, um zu wissen, dass Sie nichts über Spalten wissen möchten

Rafael Mueller
quelle
1

Der Rat, den ich von MySQL zu solchen Dingen erhalten habe, ist, dass der Versuch, eine Abfrage basierend auf solchen Tricks zu optimieren, auf lange Sicht ein Fluch sein kann. Es gibt Beispiele in der Geschichte von MySQL, in denen die Hochleistungstechnik von jemandem, die sich auf die Funktionsweise des Optimierers stützt, in der nächsten Version zum Engpass wird.

Schreiben Sie die Abfrage, die die von Ihnen gestellte Frage beantwortet. Wenn Sie alle Zeilen zählen möchten, verwenden Sie COUNT (*). Wenn Sie eine Anzahl von Nicht-Null-Spalten wünschen, verwenden Sie COUNT (col) WHERE col IS NOT NULL. Indizieren Sie entsprechend und überlassen Sie die Optimierung dem Optimierer. Der Versuch, eigene Optimierungen auf Abfrageebene vorzunehmen, kann manchmal dazu führen, dass das integrierte Optimierungsprogramm weniger effektiv ist.

Das heißt, es gibt Dinge, die Sie in einer Abfrage tun können, um es dem Optimierer zu erleichtern, sie zu beschleunigen, aber ich glaube nicht, dass COUNT eine davon ist.

Bearbeiten: Die Statistiken in der obigen Antwort sind jedoch interessant. Ich bin mir nicht sicher, ob in diesem Fall tatsächlich etwas im Optimierer am Werk ist. Ich spreche nur von Optimierungen auf Abfrageebene im Allgemeinen.

Jon
quelle
0

Ich weiß, dass es im Allgemeinen eine schlechte Idee ist, solche Abfragen durchzuführen:

SELECT * FROM `group_relations`

Aber wenn ich nur die Anzahl haben möchte, sollte ich mich für diese Abfrage entscheiden, da sich dadurch die Tabelle ändern kann, aber immer noch die gleichen Ergebnisse erzielt werden.

SELECT COUNT(*) FROM `group_relations`

Wie Ihre Frage impliziert, ist der Grund SELECT *schlecht beraten, dass Änderungen an der Tabelle Änderungen in Ihrem Code erfordern könnten. Das gilt nicht für COUNT(*). Es ist ziemlich selten, dass Sie das spezielle Verhalten wünschen, SELECT COUNT('group_id')das Sie erhalten - normalerweise möchten Sie die Anzahl der Datensätze wissen. Dafür COUNT(*)ist es da, also benutze es.

Carl Manaster
quelle