SQL: So überprüfen Sie ordnungsgemäß, ob ein Datensatz vorhanden ist

206

Beim Lesen einer Dokumentation zur SQL-Optimierung habe ich Folgendes festgestellt:

SELECT COUNT(*) ::

  • Zählt die Anzahl der Zeilen.
  • Wird häufig nicht ordnungsgemäß verwendet, um das Vorhandensein eines Datensatzes zu überprüfen.

Ist das SELECT COUNT(*)wirklich so schlimm?

Was ist der richtige Weg, um das Vorhandensein eines Datensatzes zu überprüfen?

systempuntoout
quelle

Antworten:

251

Es ist besser, eine der folgenden Methoden zu verwenden:

-- Method 1.
SELECT 1
FROM table_name
WHERE unique_key = value;

-- Method 2.
SELECT COUNT(1)
FROM table_name
WHERE unique_key = value;

Die erste Alternative sollte kein Ergebnis oder ein Ergebnis liefern, die zweite Zählung sollte null oder eins sein.

Wie alt ist die Dokumentation, die Sie verwenden? Obwohl Sie gute Ratschläge gelesen haben, optimieren die meisten Abfrageoptimierer in den neuesten RDBMS-Versionen SELECT COUNT(*)ohnehin. Obwohl es einen Unterschied in der Theorie (und älteren Datenbanken) gibt, sollten Sie in der Praxis keinen Unterschied bemerken.

Martin Schapendonk
quelle
1
Ich werde klarstellen, dass ich mit der Klausel "key = value" einen "eindeutigen Schlüssel" beabsichtigt habe, aber ansonsten stehe ich immer noch hinter meiner Antwort.
Martin Schapendonk
1
OK. Mit dieser Prämisse würde die Abfrage tatsächlich nur einen oder null Datensatz zurückgeben. ABER: Die Frage beschränkt sich nicht auf eine eindeutige Spalte. Außerdem: Die Anzahl der zweiten Abfragen (1) entspricht der Anzahl (*) eines praktischen POV.
Martin Ba
1
Die Frage lautet: "Was ist der richtige Weg, um die Existenz eines Datensatzes zu überprüfen?" Ich habe das als Singular interpretiert, wie in: 1 Datensatz. Der Unterschied zwischen Anzahl (*) und Anzahl (1) wird bereits durch meine Antwort abgedeckt. Ich bevorzuge count (1), da es nicht auf einer bestimmten RDBMS-Implementierung beruht.
Martin Schapendonk
192

Ich würde es vorziehen, die Count-Funktion überhaupt nicht zu verwenden:

IF [NOT] EXISTS ( SELECT 1 FROM MyTable WHERE ... )
     <do smth>

Wenn Sie beispielsweise überprüfen möchten, ob ein Benutzer vorhanden ist, bevor Sie ihn in die Datenbank einfügen, kann die Abfrage folgendermaßen aussehen:

IF NOT EXISTS ( SELECT 1 FROM Users WHERE FirstName = 'John' AND LastName = 'Smith' )
BEGIN
    INSERT INTO Users (FirstName, LastName) VALUES ('John', 'Smith')
END
Pavel Morshenyuk
quelle
Im Allgemeinen verwenden wir es (die Überprüfung), wenn Sie etwas tun möchten, dann ist Ihre Antwort vollständiger.
Abner Escócio
Gut zu erwähnen, dass mit T-SQL
Bronek
20

Sie können verwenden:

SELECT 1 FROM MyTable WHERE <MyCondition>

Wenn kein Datensatz vorhanden ist, der der Bedingung entspricht, ist das resultierende Datensatzset leer.

Cătălin Pitiș
quelle
Meinten Sie TOP 1? -> (WÄHLEN SIE TOP 1 AUS MyTable WHERE <MyCondition>)
Jacob
6
Nein, ich meinte genau "1"
Cătălin Pitiș
1
Damit der Abfrageoptimierer auch nur weiß, dass Sie die verbleibenden Datensätze nicht lesen / benötigen, sollten Sie SELECT TOP 1 1 FROM ... WHERE ... angeben (oder die entsprechenden Abfragehinweise für Ihr RDBS verwenden)
eFloh
3
Der Exists-Operator selbst versucht, nur das absolute Minimum an Informationen abzurufen, sodass das Hinzufügen von TOP 1 nichts anderes bewirkt, als der Abfragegröße 5 Zeichen hinzuzufügen. - sqlservercentral.com/blogs/sqlinthewild/2011/04/05/…
AquaAlex
13

Die anderen Antworten sind recht gut, aber es wäre auch nützlich, sie hinzuzufügen LIMIT 1(oder das Äquivalent , um die Überprüfung unnötiger Zeilen zu verhindern.

JesseW
quelle
3
Wenn eine Abfrage "Prüfung auf Existenz" mehr als eine Zeile zurückgibt, ist es meiner Meinung nach sinnvoller, Ihre WHERE-Klausel zu überprüfen, anstatt die Anzahl der Ergebnisse zu begrenzen.
Martin Schapendonk
2
Ich denke, Limit wird in Oracle und nicht in SQL Server verwendet
Shantanu Gupta
7
Ich betrachte den Fall, in dem es sich legitimerweise um mehrere Zeilen handeln kann - wobei die Frage lautet: "Gibt es (eine oder mehrere) Zeilen, die diese Bedingung erfüllen?" In diesem Fall möchten Sie nicht alle betrachten, sondern nur einen.
JesseW
1
@Shantanu - Ich weiß, deshalb habe ich auf den (sehr durchgehenden) en.wikipedia-Artikel verlinkt, in dem die anderen Formen erklärt werden.
JesseW
11
SELECT COUNT(1) FROM MyTable WHERE ...

wird alle Datensätze durchlaufen. Dies ist der Grund, warum es schlecht ist, für die Existenz von Aufzeichnungen zu verwenden.

ich würde ... benutzen

SELECT TOP 1 * FROM MyTable WHERE ...

Nachdem 1 Datensatz gefunden wurde, wird die Schleife beendet.

oski
quelle
Im Fall SELECT TOP 1wird es endet tatsächlich nach einer zu finden , oder ist es weiterhin alle in der Lage zu finden sein zu sagen , welches ist TOP?
Eirik H
3
PS: Um sicher zu sein, dass ich immerIF EXISTS (SELECT TOP 1 1 FROM ... WHERE ..)
Eirik H
Der Star-Operator erzwingt, dass das DBMS auf den Clustered-Index zugreift, anstatt nur auf die Indizes, die für Ihre Join-Bedingung benötigt werden. Daher ist es besser, einen konstanten Wert als Ergebnis zu verwenden, dh top 1 1 auswählen. Dies gibt 1 oder DB-Null zurück, je nachdem, ob eine Übereinstimmung vorliegt oder nicht.
eFloh
es ist schön. Ich mag den ersten.
Isxaker
10

Sie können verwenden:

SELECT COUNT(1) FROM MyTable WHERE ... 

oder

WHERE [NOT] EXISTS 
( SELECT 1 FROM MyTable WHERE ... )

Dies wird effizienter sein als SELECT * wenn Sie einfach den Wert 1 für jede Zeile und nicht für alle Felder auswählen.

Es gibt auch einen subtilen Unterschied zwischen COUNT (*) und COUNT (Spaltenname):

  • COUNT(*) zählt alle Zeilen, einschließlich Nullen
  • COUNT(column name)zählt nur Nicht-Null-Vorkommen des Spaltennamens
Winston Smith
quelle
2
Sie gehen fälschlicherweise davon aus, dass ein DBMS alle diese Spalten irgendwie überprüft. Der Leistungsunterschied zwischen count(1)und count(*)ist nur im hirntoten DBMS unterschiedlich.
Paxdiablo
2
Nein, ich sage, dass Sie sich tatsächlich auf Implementierungsdetails verlassen, wenn Sie angeben, dass dies effizienter ist. Wenn Sie wirklich sicherstellen möchten, dass Sie die beste Leistung erzielen, sollten Sie sie anhand repräsentativer Daten für die jeweilige Implementierung profilieren oder einfach ganz vergessen. Alles andere ist möglicherweise irreführend und kann sich drastisch ändern, wenn Sie beispielsweise von DB2 zu MySQL wechseln.
Paxdiablo
1
Ich möchte klarstellen, dass ich Ihre Antwort nicht ablehne. Es ist nützlich. Das einzige Problem, mit dem ich Probleme habe , ist der Effizienzanspruch, da wir Evaluierungen in DB2 / z durchgeführt haben und festgestellt haben, dass es keinen wirklichen Unterschied zwischen count(*)und gibt count(1). Ob dies bei anderen DBMS der Fall ist , kann ich nicht sagen.
Paxdiablo
3
"Alles andere ist möglicherweise irreführend und kann sich drastisch ändern, wenn Sie (zum Beispiel) von DB2 zu MySQL wechseln." Es ist viel wahrscheinlicher, dass Sie beim Verschieben von DBMS von einem Leistungsabfall von SELECT COUNT (*) gebissen werden als von einem Implementierungsunterschied in SELECT 1 oder COUNT (1). Ich bin fest davon überzeugt, den Code zu schreiben, der genau klar ausdrückt, was Sie erreichen möchten, anstatt sich auf Optimierer oder Compiler zu verlassen, um standardmäßig Ihr gewünschtes Verhalten zu erreichen.
Winston Smith
1
Die irreführende Aussage "COUNT (*)" bedeutet "Zähle die Zeilen". Es ist kein Zugriff auf eine bestimmte Spalte erforderlich. In den meisten Fällen ist nicht einmal der Zugriff auf die Zeile selbst erforderlich, da die Anzahl der eindeutigen Indizes ausreicht.
James Anderson
9

Sie können verwenden:

SELECT 1 FROM MyTable WHERE... LIMIT 1

Verwenden select 1 diese , um die Überprüfung unnötiger Felder zu verhindern.

Verwenden Sie LIMIT 1 diese Option , um die Überprüfung unnötiger Zeilen zu verhindern.

user3059943
quelle
3
Guter Punkt, aber Limit funktioniert unter MySQL und PostgreSQL. Top funktioniert unter SQL Server. Sie sollten es in Ihrer Antwort vermerken
Leo Gurdian,
0

Ich benutze diesen Weg:

IIF(EXISTS (SELECT TOP 1 1 
                FROM Users 
                WHERE FirstName = 'John'), 1, 0) AS DoesJohnExist
DiPix
quelle
0

Andere Option:

SELECT CASE
    WHEN EXISTS (
        SELECT 1
        FROM [MyTable] AS [MyRecord])
    THEN CAST(1 AS BIT) ELSE CAST(0 AS BIT)
END
Pranav
quelle