it-swarm.com.de

Anzahl (*) vs Anzahl (1) - SQL Server

Sie fragen sich nur, ob einer von Ihnen Count(1) anstelle von Count(*) verwendet und ob es einen merklichen Unterschied in der Leistung gibt oder ob dies nur eine alte Angewohnheit ist, die aus vergangenen Tagen übernommen wurde?

(Die spezifische Datenbank ist SQL Server 2005.)

681
super9

Es gibt keinen Unterschied.

Grund:

Bücher online sagt "COUNT ( { [ [ ALL | DISTINCT ] expression ] | * } )"

"1" ist ein Ausdruck, der nicht null ist. Daher ist er derselbe wie COUNT(*). Der Optimierer erkennt es als das, was es ist: trivial.

Dasselbe wie EXISTS (SELECT * ... oder EXISTS (SELECT 1 ...

Beispiel:

SELECT COUNT(1) FROM dbo.tab800krows
SELECT COUNT(1),FKID FROM dbo.tab800krows GROUP BY FKID

SELECT COUNT(*) FROM dbo.tab800krows
SELECT COUNT(*),FKID FROM dbo.tab800krows GROUP BY FKID

Gleiches IO, gleicher Plan, das funktioniert

Bearbeiten, August 2011

ähnliche Frage zu DBA.SE .

Bearbeiten, Dezember 2011

COUNT(*) wird speziell in ANSI-92 erwähnt (suchen Sie nach "Scalar expressions 125")

Fall:

a) Wenn COUNT (*) angegeben ist, ist das Ergebnis die Kardinalität von T.

Das heißt, der ANSI-Standard erkennt, dass es offensichtlich blutet, was Sie meinen. COUNT(1) wurde von RDBMS-Anbietern wegen dieses Aberglaubens optimiert. Ansonsten würde es nach ANSI ausgewertet

b) Ansonsten sei TX die einspaltige Tabelle, die das Ergebnis der Anwendung des <Werteausdrucks> auf jede Zeile von T und der Eliminierung von Nullwerten ist. Wenn ein oder mehrere Nullwerte eliminiert werden, wird eine Beendigungsbedingung ausgelöst:

565
gbn

In SQL Server ergeben diese Anweisungen dieselben Pläne.

Entgegen der landläufigen Meinung gilt dies auch für Oracle.

SYS_GUID() in Oracle ist eine recht rechenintensive Funktion.

In meiner Testdatenbank ist t_even eine Tabelle mit 1,000,000 Zeilen

Diese Abfrage:

SELECT  COUNT(SYS_GUID())
FROM    t_even

läuft 48 Sekunden lang, da die Funktion jedes zurückgegebene SYS_GUID() auswerten muss, um sicherzustellen, dass es sich nicht um ein NULL handelt.

Diese Abfrage lautet jedoch:

SELECT  COUNT(*)
FROM    (
        SELECT  SYS_GUID()
        FROM    t_even
        )

läuft nur 2 Sekunden lang, da es nicht einmal versucht, SYS_GUID() auszuwerten (obwohl * ein Argument für COUNT(*) ist)

70
Quassnoi

Es ist klar, dass COUNT (*) und COUNT (1) immer dasselbe Ergebnis liefern. Wenn daher einer langsamer als der andere wäre, würde dies effektiv auf einen Optimierungsfehler zurückzuführen sein. Da beide Formulare in Abfragen sehr häufig verwendet werden, ist es für ein DBMS nicht sinnvoll, einen solchen Fehler nicht zu beheben. Daher werden Sie feststellen, dass die Leistung beider Formen in allen wichtigen SQL-DBMS (wahrscheinlich) identisch ist.

60
Tony Andrews

Im SQL-92-Standard bedeutet COUNT(*) speziell "die Kardinalität des Tabellenausdrucks" (kann eine Basistabelle, `VIEW, abgeleitete Tabelle, CTE usw. sein).

Ich denke, die Idee war, dass COUNT(*) einfach zu analysieren ist. Bei Verwendung eines anderen Ausdrucks muss der Parser sicherstellen, dass er keine Spalten referenziert (COUNT('a') wobei a ein Literal ist und COUNT(a) wobei a eine Spalte ist, kann zu unterschiedlichen Ergebnissen führen ).

Ebenso kann COUNT(*) leicht von einem mit den SQL-Standards vertrauten menschlichen Programmierer ausgewählt werden, eine nützliche Fähigkeit, wenn mit mehr als einem SQL-Angebot eines Anbieters gearbeitet wird.

Im Sonderfall SELECT COUNT(*) FROM MyPersistedTable; wird davon ausgegangen, dass das DBMS wahrscheinlich Statistiken für die Kardinalität der Tabelle enthält.

Da COUNT(1) und COUNT(*) semantisch äquivalent sind, verwende ich COUNT(*).

20
onedaywhen

COUNT(*) und COUNT(1) sind hinsichtlich Ergebnis und Leistung identisch.

17
Nakul Chaudhary

Ich arbeite im SQL Server-Team und kann hoffentlich ein paar Punkte in diesem Thread klären (ich hatte es vorher noch nicht gesehen, es tut mir leid, dass das Engineering-Team dies noch nicht getan hat).

Erstens gibt es keinen semantischen Unterschied zwischen select count(1) from table und select count(*) from table. Sie geben in allen Fällen die gleichen Ergebnisse zurück (und es ist ein Fehler, wenn nicht). Wie in den anderen Antworten erwähnt, ist select count(column) from table semantisch unterschiedlich und gibt nicht immer die gleichen Ergebnisse wie count(*) zurück.

Zweitens gibt es in Bezug auf die Leistung zwei Aspekte, die für SQL Server (und SQL Azure) von Bedeutung sind: Kompilierungszeit und Ausführungszeit. Die Kompilierungszeitarbeit ist in der aktuellen Implementierung nur ein geringfügiger zusätzlicher Arbeitsaufwand. In einigen Fällen wird das * auf alle Spalten erweitert, gefolgt von einer Reduzierung auf 1 Spalte, die ausgegeben wird, da einige der internen Vorgänge beim Binden und Optimieren funktionieren. Ich bezweifle, dass es in einem messbaren Test auftaucht und sich wahrscheinlich im Rauschen all der anderen Dinge verliert, die unter der Decke passieren (wie Autostatistiken, xevent-Sitzungen, Overhead des Abfragespeichers, Trigger usw.). Es sind vielleicht ein paar tausend zusätzliche CPU-Anweisungen. Count (1) erledigt also ein bisschen weniger Arbeit während der Kompilierung (was normalerweise einmal vorkommt und der Plan über mehrere nachfolgende Ausführungen hinweg zwischengespeichert wird). Unter der Annahme, dass die Pläne gleich sind, sollte es für die Ausführungszeit keinen messbaren Unterschied geben. (Eines der früheren Beispiele zeigt einen Unterschied - es ist höchstwahrscheinlich auf andere Faktoren auf der Maschine zurückzuführen, wenn der Plan identisch ist.).

Wie der Plan möglicherweise anders sein kann. Es ist äußerst unwahrscheinlich, dass dies geschieht, in der Architektur des aktuellen Optimierers ist dies jedoch möglicherweise möglich. Der Optimierer von SQL Server arbeitet als Suchprogramm (denken Sie daran, dass ein Computerprogramm Schach spielt, indem es verschiedene Alternativen nach verschiedenen Abfrageteilen durchsucht und die Alternativen berechnet, um den günstigsten Plan in angemessener Zeit zu finden). Diese Suche unterliegt einigen Einschränkungen, um die Fertigstellung der Abfragekompilierung in angemessener Zeit sicherzustellen. Für Abfragen, die über das Trivialste hinausgehen, gibt es Phasen der Suche. Sie behandeln Abfragetranchen auf der Grundlage der Kosten, die der Optimierer für die potenzielle Ausführung der Abfrage hält. Es gibt 3 Hauptsuchphasen, und jede Phase kann aggressivere (teurere) Heuristiken ausführen, um einen günstigeren Plan zu finden als jede frühere Lösung. Letztendlich gibt es am Ende jeder Phase einen Entscheidungsprozess, in dem ermittelt wird, ob der bisher gefundene Plan zurückgegeben werden soll oder ob die Suche fortgesetzt werden soll. Bei diesem Vorgang wird die insgesamt bisher benötigte Zeit im Vergleich zu den geschätzten Kosten des besten bisher ermittelten Plans verwendet. Auf verschiedenen Computern mit unterschiedlichen CPU-Geschwindigkeiten ist es daher möglich (wenn auch selten), unterschiedliche Pläne zu erhalten, da in einer früheren Phase ein Zeitlimit für einen Plan festgelegt wurde und nicht in die nächste Suchphase übergegangen wird. Es gibt auch ein paar ähnliche Szenarien, die sich auf die Zeitüberschreitung in der letzten Phase beziehen und bei sehr, sehr teuren Abfragen, die den gesamten Arbeitsspeicher des Computers belegen, möglicherweise nicht mehr genügend Arbeitsspeicher zur Verfügung stehen (normalerweise kein Problem bei 64-Bit-Versionen, dies war jedoch ein größeres Problem zurück auf 32-Bit-Servern). Letztendlich würde sich die Leistung zur Laufzeit unterscheiden, wenn Sie einen anderen Plan erhalten. Ich halte es nicht für unwahrscheinlich, dass der Unterschied in der Kompilierungszeit jemals dazu führen würde, dass einer dieser Umstände eintritt.

Netz-Netz: Bitte verwenden Sie, was auch immer Sie wollen, da dies in keiner praktischen Form von Bedeutung ist. (Es gibt weitaus größere Faktoren, die die Leistung in SQL über dieses Thema hinaus beeinflussen, ehrlich gesagt).

Ich hoffe das hilft. Ich habe ein Buchkapitel darüber geschrieben, wie der Optimierer funktioniert, aber ich weiß nicht, ob es angebracht ist, ihn hier zu veröffentlichen (da ich immer noch winzige Lizenzgebühren dafür bekomme, glaube ich). Anstatt also einen Link zu einem Vortrag zu veröffentlichen, den ich bei SQLBits in Großbritannien über die Funktionsweise des Optimierers gehalten habe, können Sie die verschiedenen Hauptphasen der Suche auf Wunsch etwas detaillierter anzeigen darüber zu lernen. Hier ist der Videolink: https://sqlbits.com/Sessions/Event6/inside_the_sql_server_query_optimizer

12

Ich würde erwarten, dass der Optimierer sicherstellt, dass es keinen wirklichen Unterschied außerhalb seltsamer Edge-Fälle gibt.

Wie bei allem anderen ist die einzige Möglichkeit, dies zu beurteilen, die Messung Ihrer spezifischen Fälle.

Das heißt, ich habe immer COUNT(*) verwendet.

12
Richard

Ich habe einen kurzen Test unter SQL Server 2012 auf einer 8-GB-Hyper-V-Box RAM durchgeführt. Sie können die Ergebnisse selbst sehen. Ich habe während dieser Tests keine andere fenstergesteuerte Anwendung außer SQL Server Management Studio ausgeführt.

Mein Tabellenschema:

CREATE TABLE [dbo].[employee](
    [Id] [bigint] IDENTITY(1,1) NOT NULL,
    [Name] [nvarchar](50) NOT NULL,
 CONSTRAINT [PK_employee] PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

Gesamtzahl der Datensätze in der Tabelle Employee: 178090131 (~ 178 Millionen Zeilen)

Erste Abfrage:

Set Statistics Time On
Go    
Select Count(*) From Employee
Go    
Set Statistics Time Off
Go

Ergebnis der ersten Abfrage:

 SQL Server parse and compile time: 
 CPU time = 0 ms, elapsed time = 35 ms.

 (1 row(s) affected)

 SQL Server Execution Times:
   CPU time = 10766 ms,  elapsed time = 70265 ms.
 SQL Server parse and compile time: 
   CPU time = 0 ms, elapsed time = 0 ms.

Zweite Abfrage:

    Set Statistics Time On
    Go    
    Select Count(1) From Employee
    Go    
    Set Statistics Time Off
    Go

Ergebnis der zweiten Abfrage:

 SQL Server parse and compile time: 
   CPU time = 14 ms, elapsed time = 14 ms.

(1 row(s) affected)

 SQL Server Execution Times:
   CPU time = 11031 ms,  elapsed time = 70182 ms.
 SQL Server parse and compile time: 
   CPU time = 0 ms, elapsed time = 0 ms.

Sie können feststellen, dass es einen Unterschied von 83 (= 70265 - 70182) Millisekunden gibt, der zum Zeitpunkt der Ausführung der Abfragen leicht auf den genauen Systemzustand zurückzuführen ist. Außerdem habe ich einen einzelnen Lauf durchgeführt, sodass dieser Unterschied genauer wird, wenn ich mehrere Läufe durchführe und eine Mittelwertbildung vornehme. Wenn für einen so großen Datensatz die Differenz weniger als 100 Millisekunden beträgt, können wir leicht schließen, dass die beiden Abfragen keinen Leistungsunterschied aufweisen, der von der SQL Server-Engine angezeigt wird.

Hinweis: RAM erreicht in beiden Läufen eine Auslastung von nahezu 100%. Ich habe den SQL Server-Dienst neu gestartet, bevor ich beide Läufe gestartet habe.

8
RBT

Da diese Frage immer wieder auftaucht, gibt es hier noch eine Antwort. Ich hoffe, Anfängern, die sich hier über "Best Practice" wundern, etwas hinzuzufügen.

SELECT COUNT(*) FROM something zählt Datensätze, was eine einfache Aufgabe ist.

SELECT COUNT(1) FROM something ruft eine 1 pro Datensatz ab und zählt dann die Einsen, die nicht null sind, was im Wesentlichen Datensätze zählt, nur komplizierter.

Trotzdem: Gute DBMS bemerken, dass die zweite Anweisung dieselbe Anzahl wie die erste Anweisung ergibt und interpretieren sie entsprechend neu, um keine unnötige Arbeit zu leisten. Normalerweise führen beide Anweisungen zu demselben Ausführungsplan und benötigen dieselbe Zeit.

Unter dem Gesichtspunkt der Lesbarkeit sollten Sie jedoch die erste Anweisung verwenden. Sie möchten Datensätze zählen, also Datensätze zählen, keine Ausdrücke. Verwenden Sie COUNT (Ausdruck) nur, wenn Sie Nicht-Null-Vorkommen von etwas zählen möchten.

8
SET STATISTICS TIME ON

select count(1) from MyTable (nolock) -- table containing 1 million records. 

SQL Server-Ausführungszeiten:
CPU-Zeit = 31 ms, abgelaufene Zeit = 36 ms.

select count(*) from MyTable (nolock) -- table containing 1 million records. 

SQL Server-Ausführungszeiten:
CPU-Zeit = 46 ms, abgelaufene Zeit = 37 ms.

Ich habe dies hunderte Male ausgeführt und den Cache jedes Mal geleert. Die Ergebnisse variieren von Zeit zu Zeit, da die Serverlast variiert, aber fast immer hat count (*) eine höhere CPU-Zeit.

7
Eyal Z.

Es gibt einen Artikel , der zeigt, dass die COUNT(1) on Oracle nur ein Alias ​​für COUNT(*) ist, mit einem Beweis dafür.

Ich werde einige Teile zitieren:

Es gibt einen Teil der Datenbanksoftware namens „The Optimizer“, der in der offiziellen Dokumentation als „Integrierte Datenbanksoftware, die die effizienteste Methode zur Ausführung einer SQL-Anweisung bestimmt“ definiert ist.

Eine der Komponenten des Optimierers heißt „der Transformator“, dessen Aufgabe es ist, zu bestimmen, ob es vorteilhaft ist, die ursprüngliche SQL-Anweisung in eine semantisch äquivalente SQL-Anweisung umzuschreiben, die effizienter sein könnte.

Möchten Sie sehen, was der Optimierer macht, wenn Sie eine Abfrage mit COUNT (1) schreiben?

Mit einem Benutzer mit der Berechtigung ALTER SESSION können Sie einen tracefile_identifier einfügen, die Optimierungsablaufverfolgung aktivieren und die Auswahl COUNT(1) ausführen, z. B .: SELECT /* test-1 */ COUNT(1) FROM employees;.

Danach müssen Sie die Trace-Dateien lokalisieren, was Sie mit SELECT VALUE FROM V$DIAG_INFO WHERE NAME = 'Diag Trace'; tun können. Später in der Datei finden Sie:

SELECT COUNT(*) “COUNT(1)” FROM “COURSE”.”EMPLOYEES” “EMPLOYEES”

Wie Sie sehen, ist es nur ein Alias ​​für COUNT(*).

Ein weiterer wichtiger Kommentar: Die COUNT(*) war unter Oracle vor Oracle 7.3 wirklich schneller vor zwei Jahrzehnten :

Count (1) wurde seit 7.3 in count (*) umgeschrieben, da Oracle gerne mythische Aussagen automatisch abstimmt. In früheren Versionen von Oracle7 musste Oracle (1) für jede Zeile als Funktion auswerten, bevor DETERMINISTIC und NON-DETERMINISTIC vorhanden waren.

Vor zwei Jahrzehnten war count (*) also schneller

Für eine andere Datenbank als SQL Server sollte diese für jede Datenbank einzeln recherchiert werden.

Ich weiß, dass diese Frage spezifisch für SQL Server ist, aber die anderen Fragen zu SO zum selben Thema, ohne die Datenbank zu erwähnen, wurden geschlossen und aus dieser Antwort als dupliziert markiert.

3
Dherik

COUNT (1) unterscheidet sich nicht wesentlich von COUNT (*), wenn überhaupt. In Bezug auf die Frage, ob NULL-fähige SPALTEN gezählt werden sollen, können die Unterschiede zwischen COUNT (*) und COUNT (<some col>) auf einfache Weise demonstriert werden.

USE tempdb;
GO

IF OBJECT_ID( N'dbo.Blitzen', N'U') IS NOT NULL DROP TABLE dbo.Blitzen;
GO

CREATE TABLE dbo.Blitzen (ID INT NULL, Somelala CHAR(1) NULL);

INSERT dbo.Blitzen SELECT 1, 'A';
INSERT dbo.Blitzen SELECT NULL, NULL;
INSERT dbo.Blitzen SELECT NULL, 'A';
INSERT dbo.Blitzen SELECT 1, NULL;

SELECT COUNT(*), COUNT(1), COUNT(ID), COUNT(Somelala) FROM dbo.Blitzen;
GO

DROP TABLE dbo.Blitzen;
GO
0
Graeme