MySQL: Schnellster Weg, um die Anzahl der Zeilen zu zählen


117

Welche Art, eine Anzahl von Zeilen zu zählen, sollte in MySQL schneller sein?

Dies:

SELECT COUNT(*) FROM ... WHERE ...

Oder die Alternative:

SELECT 1 FROM ... WHERE ...

// and then count the results with a built-in function, e.g. in PHP mysql_num_rows()

Man würde denken, dass die erste Methode schneller sein sollte, da dies eindeutig ein Datenbankgebiet ist und das Datenbankmodul schneller als jeder andere sein sollte, wenn solche Dinge intern bestimmt werden.


1
Oh, ich habe eine ähnliche Frage gefunden ( stackoverflow.com/questions/1855226/… ). Aber dann benutze ich SELECT 1und nicht SELECT *. Ist da ein Unterschied?
Franz

Ich weiß es nicht, aber es ist denkbar, dass diese beiden Antworten identisch sind - der MySQL-Abfrageoptimierer kann bei jedem das Gleiche tun. das heißt, das erstere ist weniger mehrdeutig als das letztere. Warum schreibst du nicht ein paar Benchmarks und testest sie aus?
Jesse Cohen

Ähm, nehmen wir an, ich versuche, die Sichtbarkeit von SO-Suchmaschinen zu verbessern, indem ich eine ähnliche Frage mit anderen Worten stelle;)
Franz

1
Der Unterschied ist die Datenmenge, die an die PHP-Seite gesendet wird. Je mehr Spalten Sie haben, desto langsamer wird SELECT * relativ zu SELECT 1, da alle Spalten anstelle der Nummer 1 abgerufen werden. Wenn Sie beispielsweise ausführen mysql_query(), wird die gesamte Ergebnismenge von MySQL an PHP gesendet, unabhängig davon, was Sie tun mit diesen Daten machen.
Toon81

Das Stellen einer solchen Frage ist eine großartige Möglichkeit, um Einblicke oder neue Ideen zu gewinnen. Wenn Sie jedoch tatsächlich ein bestimmtes Szenario haben, in dem Sie mehr Geschwindigkeit wünschen, müssen Sie Tests durchführen, um festzustellen, was am schnellsten ist.
still_dreaming_1

Antworten:


124

Wenn Sie COUNT(*)Zählspaltenindizes aufnehmen, ist dies das beste Ergebnis. MySQL mit MyISAM- Engine speichert tatsächlich die Zeilenanzahl. Es werden nicht alle Zeilen jedes Mal gezählt, wenn Sie versuchen, alle Zeilen zu zählen. (basierend auf der Spalte des Primärschlüssels)

Die Verwendung von PHP zum Zählen von Zeilen ist nicht sehr klug, da Sie Daten von MySQL an PHP senden müssen. Warum tun Sie das, wenn Sie auf der MySQL-Seite dasselbe erreichen können?

Wenn das COUNT(*)langsam ist, sollten Sie EXPLAINdie Abfrage ausführen und prüfen, ob Indizes wirklich verwendet werden und wo sie hinzugefügt werden sollen.


Das Folgende ist nicht der schnellste Weg, aber es gibt einen Fall, in dem COUNT(*)es nicht wirklich passt - wenn Sie mit dem Gruppieren von Ergebnissen beginnen, können Probleme auftreten, bei denen COUNTnicht wirklich alle Zeilen gezählt werden.

Die Lösung ist SQL_CALC_FOUND_ROWS. Dies wird normalerweise verwendet, wenn Sie Zeilen auswählen, aber dennoch die Gesamtzahl der Zeilen kennen müssen (z. B. für das Paging). Wenn Sie Datenzeilen auswählen, fügen Sie einfach das SQL_CALC_FOUND_ROWSSchlüsselwort nach SELECT hinzu:

SELECT SQL_CALC_FOUND_ROWS [needed fields or *] FROM table LIMIT 20 OFFSET 0;

Nachdem Sie die erforderlichen Zeilen ausgewählt haben, können Sie die Anzahl mit dieser einzelnen Abfrage ermitteln:

SELECT FOUND_ROWS();

FOUND_ROWS() muss sofort nach der Datenauswahlabfrage aufgerufen werden.


Zusammenfassend hängt alles davon ab, wie viele Einträge Sie haben und was in der WHERE-Anweisung steht. Sie sollten wirklich darauf achten, wie Indizes verwendet werden, wenn viele Zeilen vorhanden sind (Zehntausende, Millionen und mehr).


14
Korrektur: MyISAMSpeichert die Zeilenanzahl. Andere Speicher-Engines wie speichern InnoDB keine Zeilenzahlen und zählen jedes Mal alle Zeilen .
Der Scrum Meister

1
Wissen Sie, welches am schnellsten ist, wenn Sie einfach herausfinden möchten, ob es eine Zeile gibt: SELECT 1 FROM ... LIMIT 1oder SELECT COUNT(*) FROM ...?
Franz

1
Es ist wahrscheinlich nützlich zu beachten, dass, wenn Sie die Daten trotzdem benötigen und nur eine Zählung für Paginierung / etc. Wünschen. Es ist effizienter, die Daten abzurufen, als die Zeilen in Ihrem Programm zu zählen.
Tyzoid

6
Es ist unerheblich, ob der Motor die Anzahl der Zeilen speichert. Die Frage besagt eindeutig, dass es eine WHEREKlausel gibt.
Álvaro González

1
@Franz SELECT COUNT(*) FROM ...kann viel Zeit in Anspruch nehmen, je nachdem, was gescannt werden muss (z. B. eine sehr große Tabelle oder ein Index von Millionen / Milliarden / Billionen Zeilen). SELECT 1 FROM ... LIMIT 1kehrt sofort zurück, da Sie es auf die erste Zeile beschränken.
jbo5112

59

Nachdem Ricardo mit meinen Teamkollegen gesprochen hatte, sagte er uns, dass der schnellere Weg ist:

show table status like '<TABLE NAME>' \G

Sie müssen sich jedoch daran erinnern, dass das Ergebnis möglicherweise nicht genau ist.

Sie können es auch über die Befehlszeile verwenden:

$ mysqlshow --status <DATABASE> <TABLE NAME>

Weitere Informationen: http://dev.mysql.com/doc/refman/5.7/en/show-table-status.html

Eine vollständige Diskussion finden Sie auf mysqlperformanceblog


2
Für InnoDB ist dies eine Annäherung.
Martin Tournoij

2
Dies ist gut zu wissen, wenn Sie eine ungefähre Vorstellung von der Anzahl der Zeilen in sehr großen Tabellen benötigen, in denen count (*) buchstäblich Stunden dauern kann!
Mark Hansen

Dies rettete mich davor, alle meine Haare herauszuziehen. COUNT (*) brauchte Ewigkeiten, um alle mehr als 33 Millionen Zeilen in meiner Datenbank zu zählen. Auf jeden Fall wollte ich nur wissen, ob meine Funktion zum parallelisierten Löschen von Zeilen funktioniert oder nicht. Ich brauchte keine genaue Nummer.
joemar.ct

1
+1 Wenn Sie stattdessen den Tabellenstatus verwenden, sollte "COUNT (*)" die richtige Antwort auf diese Frage sein, da es um "schnellste" und nicht um "Genauigkeit" geht.
Lepe

2
Die Verwendung SHOW TABLE STATUS(oder das Äquivalent SELECTin information_schema) ist schnell, behandelt jedoch keine WHEREKlausel. Es ist präzise für MyISAM, aber für InnoDB ungenau (manchmal um den Faktor 2).
Rick James

29

Tolle Frage, tolle Antworten. Hier ist eine schnelle Möglichkeit, die Ergebnisse wiederzugeben, wenn jemand diese Seite liest und diesen Teil vermisst:

$counter = mysql_query("SELECT COUNT(*) AS id FROM table");
$num = mysql_fetch_array($counter);
$count = $num["id"];
echo("$count");

5
mysql_query ist eine veraltete Funktion ab PHP 5.5.0.
Omar Tariq

8
Warum nicht as count? idist auf den ersten Blick verwirrend.
Orkhan Alikhanov

Beantwortet die Frage nicht
mentalic

17

Diese Abfrage (ähnlich der von Bayuah geposteten ) zeigt eine schöne Zusammenfassung aller Tabellen in einer Datenbank: (vereinfachte Version der gespeicherten Prozedur von Ivan Cachicatari, die ich sehr empfehlen kann).

SELECT TABLE_NAME AS 'Table Name', TABLE_ROWS AS 'Rows' FROM information_schema.TABLES WHERE TABLES.TABLE_SCHEMA = '`YOURDBNAME`' AND TABLES.TABLE_TYPE = 'BASE TABLE'; 

Beispiel:

+-----------------+---------+
| Table Name      | Rows    |
+-----------------+---------+
| some_table      |   10278 |
| other_table     |     995 |

Es gibt mir ein Ergebnis. Die Ergebnisse von count (1) und diesem sind jedoch unterschiedlich. Auf diese Weise erhalten Sie immer eine geringere Anzahl als die Anzahl der Abfragen. Irgendwelche Gedanken?
Ayyappan Sekar

3
Nur eine Notiz an die Leser. Diese Methode ist extrem schnell, aber nur anwendbar, wenn Sie mit einer ungefähren Anzahl von Zeilen arbeiten können, da der darin gespeicherte Wert information_schemanicht mit dem Wert übereinstimmt, der von SELECT count(*) FROMInnoDB zurückgegeben wird, wenn InnoDB verwendet wird. Wenn Sie einen strengen Wert benötigen, beachten Sie, dass diese Methode nur bei MyISAM-Tabellen einen strengen Wert liefert. Mit InnoDB ist die Anzahl der Zeilen eine grobe Annäherung.
Bartosz Firyn

13

Ich habe immer verstanden, dass das Folgende mir die schnellsten Antwortzeiten gibt.

SELECT COUNT(1) FROM ... WHERE ...

1
Wäre SELECT 1 FROM ... WHERE ... nicht noch schneller?
Patrick

3
@patrick - SELECT 1 ...gibt so viele Zeilen zurück wie die WHEREund LIMITfragen nach, und alle werden "1" sein.
Rick James

1
show table status like '<TABLE NAME>' Dies wird viel schneller sein.
Tief

@deep - aber nicht relevant, wenn Sie eine WHEREKlausel haben. Und für InnoDB ist es nur eine Schätzung.
Rick James

@ RickJames ja wahr!
Tief

6

Wenn Sie die Anzahl der gesamten Ergebnismenge abrufen müssen, können Sie wie folgt vorgehen:

SELECT SQL_CALC_FOUND_ROWS * FROM table_name LIMIT 5;
SELECT FOUND_ROWS();

Dies ist normalerweise nicht schneller als die Verwendung, COUNTobwohl man denken könnte, dass das Gegenteil der Fall ist, da die Berechnung intern durchgeführt wird und die Daten nicht an den Benutzer zurückgesendet werden, sodass die Leistungsverbesserung vermutet wird.

Das Ausführen dieser beiden Abfragen ist gut für die Paginierung, um Summen zu erhalten, aber nicht besonders für die Verwendung von WHEREKlauseln.


Interessant. Funktioniert das auf den gängigsten Datenbanksystemen? MySQL, Postgres, SQLite ...?
Franz

4
Dies ist tatsächlich oft nicht schneller als die Verwendung von COUNT (*) überhaupt. Siehe stackoverflow.com/questions/186588/…
toon81

2
Sie sollten bei der Verwendung dieser Funktion SEHR vorsichtig sein. Sein rücksichtsloser Einsatz brachte einst unsere gesamte Produktionsumgebung zum Erliegen. Es ist SEHR ressourcenintensiv, verwenden Sie es daher mit Vorsicht.
Janis Peisenieks

6

Ich habe einige Benchmarks durchgeführt , um die Ausführungszeit von COUNT(*)vs zu vergleichen COUNT(id)(id ist der Primärschlüssel der Tabelle - indiziert).

Anzahl der Versuche: 10 * 1000 Anfragen

Ergebnisse: COUNT(*)ist schneller 7%

VIEW GRAPH: Benchmarkgraph

Mein Rat ist zu verwenden: SELECT COUNT(*) FROM table


1
Zu COUNT(1)
Ihrer Information,

4

Versuche dies:

SELECT
    table_rows "Rows Count"
FROM
    information_schema.tables
WHERE
    table_name="Table_Name"
AND
    table_schema="Database_Name";

@lepe Es tut mir leid. Ich meinte, es ist wirklich schön, wenn jemand, der Downvoting gemacht hat, eine Erklärung gibt, warum er / sie das tut, damit jeder etwas darüber lernen kann.
Bayuah

1
Dies gibt Ihnen schnell eine ungefähre Antwort. Wenn Sie eine genaue Antwort benötigen, müssen Sie select count(*) from table_nameetwas anderes ausführen . dba.stackexchange.com/questions/151769/…
Programster

@Programster Danke. Es ist besser, als mich fast ein Jahr im Dunkeln zu lassen.
Bayuah

1
@bayuah Ich bin mir nicht sicher, was du mit deinem letzten Kommentar gemeint hast. Ich kann nur annehmen, dass Sie denken, ich bin derjenige, der Ihre Antwort abgelehnt hat, was ich nicht bin.
Programster

1
@Programster Nein, tut mir leid, das habe ich nicht gemeint. Ich wollte mich für Ihre Erklärung bedanken, damit ich vermuten kann, was Downvoter vielleicht gedacht hat, als er / sie das tat.
Bayuah

3

Vielleicht möchten Sie eine SELECT max(Id) - min(Id) + 1. Dies funktioniert nur, wenn Ihre IDs sequentiell sind und Zeilen nicht gelöscht werden. Es ist jedoch sehr schnell.


3
Seien Sie vorsichtig: Server verwenden manchmal einen automatischen Inkrementwert> 1 (aus Sicherungsgründen), daher ist diese Lösung gut, aber Sie sollten zuerst Ihre DB-Konfiguration überprüfen.
Alex

1

EXPLAIN SELECT id FROM ....hat den Trick für mich gemacht. und ich konnte die Anzahl der Zeilen unter der rowsSpalte des Ergebnisses sehen.


0

Ich habe Tische für die Bundesregierung mit manchmal 60 Millionen Datensätzen bearbeitet.

Und wir mussten die Gesamtzahl der Zeilen um ein Vielfaches kennen.

Deshalb haben wir Datenbankprogrammierer entschieden, dass in jeder Tabelle Datensatz eins immer der Datensatz ist, in dem die gesamten Datensatznummern gespeichert sind. Wir haben diese Nummer abhängig von den Zeilen INSERT oder DELETE aktualisiert.

Wir haben alle anderen Möglichkeiten ausprobiert. Dies ist bei weitem der schnellste Weg.


1
und wie sind die Details, wie Sie diese Zeile aktualisiert haben? Dies bedeutet jedoch ein fehlerhaftes Design für eine Tabelle, bei der für alle Zeilen ein verschwendeter Int erforderlich wäre, um für die Fahrt mitzukommen.
Drew

5
Ja, das ist wirklich dumm, haha. Bei jeder Abfrage müssen Sie die erste Zeile ignorieren. Ich würde einfach eine Summentabelle erstellen und diese basierend auf einem Trigger füllen. Benutzertabelle beim Einfügen, Summen-Tabelle aktualisieren. Benutzertabelle beim Löschen, Summen-Tabelle aktualisieren.
HTMLGuy

-1

Eine count (*) -Anweisung mit einer where-Bedingung auf dem Primärschlüssel gab die Zeilenanzahl für mich viel schneller zurück, um einen vollständigen Tabellenscan zu vermeiden.

SELECT COUNT(*) FROM ... WHERE <PRIMARY_KEY> IS NOT NULL;

Das war für mich viel schneller als

SELECT COUNT(*) FROM ...
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.