Der schnellste Weg, um festzustellen, ob ein Datensatz vorhanden ist

143

Wie der Titel schon sagt ... Ich versuche, den schnellsten Weg mit dem geringsten Aufwand herauszufinden, um festzustellen, ob ein Datensatz in einer Tabelle vorhanden ist oder nicht.

Beispielabfrage:

SELECT COUNT(*) FROM products WHERE products.id = ?;

    vs

SELECT COUNT(products.id) FROM products WHERE products.id = ?;

    vs

SELECT products.id FROM products WHERE products.id = ?;

Angenommen, die ?wird ausgetauscht mit 'TB100'... Sowohl die erste als auch die zweite Abfrage geben genau das gleiche Ergebnis zurück (sagen wir ... 1für diese Konversation). Die letzte Abfrage wird 'TB100'wie erwartet zurückgegeben oder nichts, wenn die idnicht in der Tabelle vorhanden ist.

Der Zweck ist herauszufinden, ob das idin der Tabelle ist oder nicht. Wenn nicht, fügt das Programm als nächstes den Datensatz ein. Wenn dies der Fall ist, überspringt das Programm ihn oder führt eine UPDATE-Abfrage basierend auf einer anderen Programmlogik außerhalb des Bereichs dieser Frage durch.

Was ist schneller und hat weniger Overhead? (Dies wird zehntausend Mal pro Programmlauf wiederholt und mehrmals täglich ausgeführt.)

(Ausführen dieser Abfrage für M $ SQL Server von Java über den von M $ bereitgestellten JDBC-Treiber)

SnakeDoc
quelle
1
Dies kann datenbankabhängig sein. Zum Beispiel ist das Zählen auf Postgres ziemlich langsam.
Mike Christensen
Entschuldigung, dies ist Java, das über den JDBC-Treiber mit M $ SQL spricht. Ich werde mein OP aktualisieren.
SnakeDoc
2
Es gibt auch.
Nikola Markovinović
@ Nikola Markovinović: Wie würden Sie es in diesem Fall verwenden?
Zerkms
5
@zerkms Abhängig vom Kontext. Wenn in gespeicherter Prozedur wäre es if exists(select null from products where id = @id); wenn in einer Abfrage direkt von einem Client aufgerufen select case when exists (...) then 1 else 0 end.
Nikola Markovinović

Antworten:

170

SELECT TOP 1 products.id FROM products WHERE products.id = ?; übertrifft alle Ihre Vorschläge, da die Ausführung beendet wird, nachdem der erste Datensatz gefunden wurde.

Declan_K
quelle
5
Berücksichtigt der Optimierer dies nicht selbst, wenn er PK (oder einen anderen eindeutigen Schlüssel) durchsucht?
Zerkms
3
Er hat nie angegeben, dass dies die PK ist, aber wenn ja, dann würde der Optimierer dies berücksichtigen.
Declan_K
3
@Declan_K: Anscheinend ist meine magische Kugel in diesem Fall ausgefallen und eine Spalte mit dem Titel "Ist idnicht PK". Also +1 zu deinem Rat.
Zerkms
4
Wenn es nicht die PK ist, würde ich auch vorschlagen, sicherzustellen, dass es einen Index für diese Spalte gibt. Andernfalls muss die Abfrage einen Tabellenscan anstelle einer schnelleren Tabellensuche durchführen.
CD Jorgensen
3
Ich denke, wir sollten eine Antwort von @ nenad-zivkovic über diese Frage in Betracht ziehen.
Giulio Caccin
191

EXISTS(oder NOT EXISTS) wurde speziell entwickelt, um zu überprüfen, ob etwas vorhanden ist, und sollte daher die beste Option sein (und ist). Es wird in der ersten übereinstimmenden Zeile angehalten, sodass keine TOPKlausel erforderlich ist und keine Daten ausgewählt werden, sodass die Spaltengröße keinen Overhead verursacht. Sie können sicher verwenden SELECT *hier - nicht anders als SELECT 1, SELECT NULLoder SELECT AnyColumn... (man kann sogar einen ungültigen Ausdruck wie verwenden SELECT 1/0und es wird nicht brechen) .

IF EXISTS (SELECT * FROM Products WHERE id = ?)
BEGIN
--do what you need if exists
END
ELSE
BEGIN
--do what needs to be done if not
END
Nenad Zivkovic
quelle
Muss dies nicht zuerst die SELECT-Anweisung und dann die IF EXISTS-Anweisung ausführen ... was zusätzlichen Overhead und damit mehr Verarbeitungszeit verursacht?
SnakeDoc
7
@SnakeDoc No. Existsarbeitet selectso, dass es beendet wird, sobald eine Zeile gefunden wird. Darüber hinaus wird lediglich das Vorhandensein eines Datensatzes und nicht der tatsächlichen Werte im Datensatz vermerkt, sodass die Zeile nicht mehr von der Festplatte geladen werden muss (vorausgesetzt, die Suchkriterien sind indiziert). Was den Overhead von if- angeht, müssen Sie diese winzige Zeit sowieso verbringen.
Nikola Markovinović
1
@ NikolaMarkovinović interessanter Punkt. Ich bin mir nicht sicher, ob in diesem Feld ein Index vorhanden ist, und mein neuer SQL weiß nicht, wie er es herausfinden soll. Ich arbeite mit dieser Datenbank von Java über JDBC und die Datenbank befindet sich irgendwo in einer Farbe. Ich habe nur eine "Datenbankzusammenfassung" erhalten, in der nur angegeben ist, welche Felder in jeder Tabelle vorhanden sind, welcher Typ und welche FKs oder PKs vorhanden sind. Ändert das etwas?
SnakeDoc
3
@SnakeDoc Um Informationen zur Tabellenstruktur einschließlich Fremdschlüsseln und Indizes zu erhalten, führen Sie sp_help table_name aus . Indizes sind wichtig, wenn es darum geht, einige von vielen Zeilen abzurufen, ob mit select topoder exists; Wenn sie nicht vorhanden sind, muss die SQL-Engine einen Tabellenscan durchführen. Dies ist die am wenigsten wünschenswerte Tabellensuchoption. Wenn Sie nicht berechtigt sind, Indizes zu erstellen, müssen Sie den technischen Mitarbeitern auf der anderen Seite mitteilen, ob sie diese automatisch anpassen oder von Ihnen erwarten, dass Sie Indizes vorschlagen.
Nikola Markovinović
1
@Konstantin Sie können etwas tun wieSELECT CASE WHEN EXISTS(..) THEN 1 ELSE 0 END;
Nenad Zivkovic
21

Nichts kann schlagen -

SELECT TOP 1 1 FROM products WHERE id = 'some value';

Sie müssen nicht zählen, um zu wissen, ob Daten in der Tabelle vorhanden sind. Und verwenden Sie keinen Alias, wenn dies nicht erforderlich ist.

AgentSQL
quelle
5
Trotz seines Namens idist kein Primärschlüssel. Auch wenn Sie nicht zählen, müssen Sie dennoch alle übereinstimmenden Datensätze finden, möglicherweise Tausende davon. Über Aliasing - Code ist ständig in Arbeit. Sie wissen nie, wann Sie zurück müssen. Aliasing hilft, dumme Laufzeitfehler zu vermeiden. Beispielsweise ist ein eindeutiger Spaltenname , für den kein Alias ​​erforderlich ist, nicht mehr eindeutig, da jemand eine gleichnamige Spalte in einer anderen verknüpften Tabelle erstellt hat.
Nikola Markovinović
Ja, du hast absolut recht. Aliasing hilft sehr, aber ich glaube nicht, dass es einen Unterschied macht, wenn keine Joins verwendet werden. Also sagte ich, benutze es nicht, wenn es nicht nötig ist. :) Und Sie können eine lange Diskussion finden hier bei der Kontrolle der Existenz. :)
AgentSQL
3
Ich weiß nicht, warum ich den Begriff akzeptiert habe aliasing. Richtiger Begriff ist qualifying. Hier ist eine längere Erklärung von Alex Kuznetzov . Informationen zu Abfragen einzelner Tabellen - es handelt sich jetzt um einzelne Tabellen . Aber später, wenn ein Fehler entdeckt wird und Sie versuchen, die Flut zu halten, ist der Client nervös. Sie setzen sich an einen anderen Tisch, um eine Fehlermeldung zu erhalten - eine leicht korrigierbare Meldung, aber nicht in diesem schweißtreibenden Moment, in der ein kleiner Schlaganfall auftritt - und Sie korrigieren die Fehler beim Erinnern daran, niemals eine Kolumne zu verlassen ...
Nikola Markovinović
1
Kann das jetzt nicht ignorieren. Vielen Dank!! :)
AgentSQL
15
SELECT CASE WHEN EXISTS (SELECT TOP 1 *
                         FROM dbo.[YourTable] 
                         WHERE [YourColumn] = [YourValue]) 
            THEN CAST (1 AS BIT) 
            ELSE CAST (0 AS BIT) END

Dieser Ansatz gibt einen Booleschen Wert für Sie zurück.

Kris Coleman
quelle
1
Kann wahrscheinlich die Top-Anweisung und die * -Anweisung weglassen, um sie etwas schneller zu machen, da Exist beendet wird, sobald ein Datensatz gefunden wird. Gehen Sie also wie folgt vor: SELECT CASE WHEN EXISTS (SELECT 1 FROM dbo. [YourTable] WHERE [YourColumn] = [YourValue]) DANN CAST (1 AS BIT) ELSE CAST (0 AS BIT) ENDE
Stefan Zvonar
In diesem Vorschlag wird nicht erwähnt, warum dies schneller sein würde, wenn die integrierten Anweisungen in SQL Server vorhanden sind / nicht vorhanden sind. Ohne Benchmarking würde ich kaum glauben können, dass eine Fallaussage zu einem schnelleren Ergebnis führen würde als eine sofortige wahre / falsche Antwort.
Bonez024
8

Sie können auch verwenden

 If EXISTS (SELECT 1 FROM dbo.T1 WHERE T1.Name='Scot')
    BEGIN
         --<Do something>
    END 

ELSE    
     BEGIN
       --<Do something>
     END
Atik Sarker
quelle
7

Denken Sie nicht, dass es bereits jemand erwähnt hat, aber wenn Sie sicher sind, dass sich die Daten unter Ihnen nicht ändern, können Sie auch den NoLock-Hinweis anwenden, um sicherzustellen, dass sie beim Lesen nicht blockiert werden.

SELECT CASE WHEN EXISTS (SELECT 1 
                     FROM dbo.[YourTable] WITH (NOLOCK)
                     WHERE [YourColumn] = [YourValue]) 
        THEN CAST (1 AS BIT) 
        ELSE CAST (0 AS BIT) END
Stefan Zvonar
quelle
3
SELECT COUNT(*) FROM products WHERE products.id = ?;

Dies ist die relationale Datenbanklösung, die in allen Datenbanken funktioniert.

Schurkenjunge
quelle
6
Allerdings zwingen Sie die Datenbank, alle Datensätze
amd
@amd möchte erklären, warum?
UmNyobe
@amd Ihr Kommentar macht total Sinn. Diese Abfrage ist eher FIND ALL als FIND ANY.
UmNyobe
1

Im Folgenden finden Sie die einfachste und schnellste Methode, um festzustellen, ob ein Datensatz in der Datenbank vorhanden ist oder nicht. Gut, dass er in allen relationalen DBs funktioniert

SELECT distinct 1 products.id FROM products WHERE products.id = ?;
manischer Prasad
quelle
0
create or replace procedure ex(j in number) as
i number;
begin
select id into i from student where id=j;
if i is not null then
dbms_output.put_line('exists');
end if;
exception
   when no_data_found then
        dbms_output.put_line(i||' does not exists');

end;
kiran
quelle
2
Möglicherweise funktioniert Ihr Code hervorragend, aber es ist besser, wenn Sie zusätzliche Informationen hinzufügen, damit diese besser verständlich sind.
idmean
0

Ich habe dies in der Vergangenheit verwendet und es ist kein vollständiger Tabellenscan erforderlich, um festzustellen, ob etwas vorhanden ist. Es ist super schnell ...

UPDATE TableName SET column=value WHERE column=value
IF @@ROWCOUNT=0
BEGIN
     --Do work
END             
Eric Parsons
quelle
0

Für diejenigen, die aus MySQL- oder Oracle-Hintergrund darauf stoßen - MySQL unterstützt die LIMIT-Klausel, um eine begrenzte Anzahl von Datensätzen auszuwählen, während Oracle ROWNUM verwendet.

Werner
quelle