SELECT COUNT(*) vs. SELECT COUNT(1)

By Frank Kalis

Posted on Jan 25, 2011 von in SQL Server

Immer wieder kann man die Datenbank Communities die Behauptung lesen, dass ein SELECT (1) schneller sein soll als ein SELECT COUNT(*).

...und immer wieder muss man dem entgegenhalten, dass dies - zumindest für SQL Server - nicht stimmt. Ich kenne mich zwar nicht so mit vielen anderen Datenbank Plattformen aus, würde aber argumentieren, dass es ein Bug im Optimierer wäre, wenn es einen Unterschied machen sollte und dass dieser Bug schnellstens gefixed werden würde.

Wie gesagt, für SQL Server gibt es KEINEN Unterschied zwischen beiden Varianten. Ein Blick in die SQL Server Books Online zu COUNT zeigt folgendes:

COUNT ( { [ [ ALL | DISTINCT ] expression ] | * } )

und etwas weiter unten

* Gibt an, dass alle Zeilen gezählt werden müssen, um die Gesamtzahl der Zeilen einer Tabelle zurückzugeben. COUNT(*) besitzt keine Parameter und kann nicht zusammen mit DISTINCT verwendet werden. COUNT(*) erfordert keinen expression-Parameter, da definitionsgemäß keine Informationen zu einer bestimmten Spalte verwendet werden. COUNT(*) gibt die Anzahl von Zeilen in der angegebenen Tabelle zurück, ohne Duplikate zu entfernen. Dabei werden alle Zeilen einzeln gezählt. Dies gilt auch für Zeilen, die NULL-Werte enthalten.

Das heisst, dass SQL Server nicht zu den Metadaten gehen muss, um Informationen zu Spalten usw. abzufragen, da bekannt ist, dass dieses * im Kontext eines COUNTs aufgerufen wird und deshalb Spalteninformationen nicht von Interesse sind. Daher wäre es eine offensichtliche Verschwendung von Resourcen, Informationen abzufragen, die niemand benötigt. Die 1 in COUNT(1) ist "nur" ein Ausdruck, der identisch zu * interpretiert wird, da der Ausdruck 1 nie NULL ist.

Selbst gesetzt den Fall, dass Metadaten abgefragt werden sollten, würde dies vor der eigentlichen Ausführungsphase erfolgen und hätte damit nur einen Effekt auf die Kompilier-/Optimier-Phase. Die eigentliche Ausführungsphase hingegen ist in beiden Fällen absolut identisch. Gleicher Ausführungsplan, gleicher IO, gleiches alles.

Hier einige der vielen Diskussionen zu diesem Thema:

http://stackoverflow.com/q/1221559/27535

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

Und hier ein Link zu Oracle, in der Tom Kyte klarstellt, dass es auch für Oracle keinen Unterschied zwischen beiden Varianten gibt:

http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:1156151916789

Man kann sogar Ausdrücke verwenden, die im Regelfall sonst einen Fehler verursachen würden, wie zum Beispiel SELECT COUNT(1/0). Zumindest scheint es solange zu funktionieren, wie kein GROUP BY eingefügt wird. SELECT COUNT(1/0) ... GROUP BY whatever fällt nämlich interessanterweise auf die Nase. :-)

Tags: Tags: , , ,
Dieser Eintrag wurde eingetragen von und ist abgelegt unter SQL Server. Tags: , , ,

3 Kommentare

Thomas
Hallo Frank, diese Regel, dass "SELECT COUNT(1)" schneller ist als "SELECT COUNT(*)" galt am SQL Server aber tatsächlich mal. Erst recht spät wurde hier im Optimizer eine Sonderregel eingebaut. Jetzt muss ich tippen, denn meine alten Unterlagen warf ich vor ein paar Monaten weg: Ich bin aber ziemlich sicher, dass diese Regel wenigstens noch für die Version SQL Server 4.2 galt. Und ich meine, dass Microsoft die Sonderbehandlung zuerst einbaute (vermutlich in Version 6.5 oder wahrscheinlicher Version 7.0), danach erst Sybase (vermutlich Version 11). Als wir unsere ersten Firmen-TSQL-Richtlinien verfassten, da galt das jedenfalls wirklich noch. Dementsprechend war das auch in vielen System-Prozeduren des SQL-Servers drin (vermutlich alle, die die ursprünglich noch von Sybase stammten ;-) ). Als Indiz kann man werten, dass Sybase-Mitarbeiter das Konstrukt auch 1998 noch benutzten. Such mal im "Microsoft SQL Server to ASE Migration Guide" (http://m.sybase.com/content/1003133/sql_to_ase_guide.pdf) nach "SELECT COUNT". Viele Grüße Thomas PS: Schade, dass in Deinen Kommentaren keine Links erlaubt sind.
31.01.11 @ 21:59
Hi Thomas, es würde mich nicht wirklich überraschen. :-) Wahrscheinlich ist es ähnlich dem EXISTS (SELECT * ...) vs. EXISTS (SELECT 1 ...). Wenn mich jetzt mein Gedächtnis nicht trügt, war es zumindest bis SQL Server 2000 (2005?) so, dass der Parser ein * immer in die tatsächliche Spaltenliste entfaltete, nur um dann festzustellen, dass dieses * im Kontext eines EXISTS stand und die Liste dann gleich wieder verwarf, da sie nicht von Interesse war. Dies mochte einen minimalen Effekt auf die Parse- und Kompilierphase gehabt haben, der daraus resultierende Plan war aber in beiden Fällen identisch. Conor Cunningham hat dies mal so beschrieben: "It is functionally equivalent from the end plan perspective. For compilation time, it may be ever-so-slightly faster to use "SELECT 1 FROM T" because it does not need to expand the * into a column list and then prune it away. Obviously it would matter more on tables with more columns. Select '1' is also fine. I use SELECT 1 since it's the smallest thing I can do in an exists subquery - fewer allocations ;). Hope that helps. Conor Cunningham SQL Server Query Optimization Development Lead Microsoft" Zu finden hier. Ich weiss jetzt nicht, ob SQL Server mittlerweile "schlauer" geworden ist, aber als Relikt daraus, habe ich mir angewöhnt EXISTS (SELECT 1...) zu verwenden. Bei COUNT(1) ist das anders. Das habe ich eigentlich nie verwendet. Vielleicht aus deshalb, weil ich selten ein COUNT(*) benutze. Sorry, aber es hat mit Spam zu tun, dass ich fast nur Plain-Text in Kommentaren erlaube. Du kannst aber einfach den Link einfügen. Ich kann den dann als solchen formatieren, bevor ich den Kommentar veröffentliche.
01.02.11 @ 09:09


Formular wird geladen...