Le moyen le plus rapide de déterminer si un enregistrement existe

143

Comme le titre l'indique ... j'essaie de trouver le moyen le plus rapide avec le moins de frais généraux pour déterminer si un enregistrement existe dans une table ou non.

Exemple de requête:

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 = ?;

Disons que le ?est échangé avec 'TB100'... les première et deuxième requêtes renverront exactement le même résultat (disons ... 1pour cette conversation). La dernière requête retournera 'TB100'comme prévu, ou rien si le idn'est pas présent dans la table.

Le but est de savoir si le idest dans le tableau ou non. Sinon, le programme insérera ensuite l'enregistrement, si tel est le cas, le programme l'ignorera ou effectuera une requête UPDATE basée sur une autre logique de programme en dehors du champ d'application de cette question.

Lequel est le plus rapide et a moins de frais généraux? (Cela sera répété des dizaines de milliers de fois par exécution de programme et sera exécuté plusieurs fois par jour).

(Exécution de cette requête sur M $ SQL Server à partir de Java via le pilote JDBC fourni par M $)

SnakeDoc
la source
1
Cela peut dépendre de la base de données. Par exemple, compter sur Postgres est plutôt lent.
Mike Christensen
Désolé, c'est Java qui parle à M $ SQL via le pilote jdbc. Je mettrai à jour mon OP.
SnakeDoc
2
Il existe aussi.
Nikola Markovinović
@Nikola Markovinović: comment l'utiliseriez-vous dans ce cas?
zerkms
5
@zerkms Dépend du contexte. Si dans la procédure stockée ce serait if exists(select null from products where id = @id); si dans une requête appelée directement par un client select case when exists (...) then 1 else 0 end.
Nikola Markovinović

Réponses:

170

SELECT TOP 1 products.id FROM products WHERE products.id = ?; dépassera toutes vos suggestions car il mettra fin à l'exécution après avoir trouvé le premier enregistrement.

Declan_K
la source
5
L'optimiseur ne le prend-il pas en compte lors des recherches via PK (ou toute autre clé unique)?
zerkms
3
Il n'a jamais déclaré que c'était le PK, mais si oui, l'optimiseur en tiendrait compte.
Declan_K
3
@Declan_K: il semble que ma sphère magique a échoué dans ce cas et une colonne intitulée comme idn'est pas PK. Donc +1 à votre avis.
zerkms
4
Si ce n'est pas le PK, je suggérerais également de m'assurer qu'il y a un index sur cette colonne. Sinon, la requête devra effectuer une analyse de table au lieu d'une recherche de table plus rapide.
CD Jorgensen
4
Je pense que nous devrions envisager la réponse de @ nenad-zivkovic sur celle-ci.
Giulio Caccin
193

EXISTS(ou NOT EXISTS) est spécialement conçu pour vérifier si quelque chose existe et devrait donc être (et est) la meilleure option. Il s'arrêtera sur la première ligne qui correspond, donc il ne nécessite pas de TOPclause et il ne sélectionne en fait aucune donnée donc il n'y a pas de surcharge en taille des colonnes. Vous pouvez utiliser en toute sécurité SELECT *ici - pas différent de SELECT 1, SELECT NULLou SELECT AnyColumn... (vous pouvez même utiliser une expression invalide comme SELECT 1/0et elle ne cassera pas) .

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
la source
cela ne doit-il pas d'abord exécuter l'instruction SELECT, puis exécuter l'instruction IF EXISTS ... entraînant une surcharge supplémentaire et donc plus de temps de traitement?
SnakeDoc
7
@SnakeDoc No. Existsfonctionne avec selectde telle manière qu'il se termine dès qu'une ligne est trouvée. De plus, il existe simplement des notes sur l'existence d'un enregistrement, pas de valeurs réelles dans l'enregistrement, ce qui évite de charger la ligne à partir du disque (en supposant que les critères de recherche sont indexés, bien sûr). En ce qui concerne les frais généraux de if- vous devrez de toute façon passer ce temps minuscule.
Nikola Markovinović
1
@ NikolaMarkovinović point intéressant. Je ne suis pas sûr qu'un index existe sur ce champ, et mon nouveau SQL ne sait pas comment le découvrir. Je travaille avec cette base de données de Java via JDBC et la base de données est située à distance dans une colo quelque part. On ne m'a fourni qu'un "résumé de la base de données" qui détaille simplement quels champs existent dans chaque table, leur type et tout FK ou PK. Cela change-t-il quelque chose?
SnakeDoc
3
@SnakeDoc Pour en savoir plus sur la structure des tables, y compris les clés étrangères et les index, exécutez sp_help nom_table . Les index sont essentiels lorsqu'il s'agit de récupérer quelques lignes parmi d'autres, en utilisant select topou exists; s'ils ne sont pas présents, le moteur SQL devra effectuer une analyse de table. C'est l'option de recherche de table la moins souhaitable. Si vous n'êtes pas autorisé à créer des index, vous devrez communiquer avec le personnel technique de l'autre côté pour savoir s'ils les ajustent automatiquement ou s'ils s'attendent à ce que vous suggériez des index.
Nikola Markovinović
1
@Konstantin Vous pouvez faire quelque chose commeSELECT CASE WHEN EXISTS(..) THEN 1 ELSE 0 END;
Nenad Zivkovic
21

Rien ne peut battre -

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

Vous n'avez pas besoin de compter pour savoir s'il y a des données dans le tableau. Et n'utilisez pas d'alias lorsque cela n'est pas nécessaire.

AgentSQL
la source
5
Malgré son nom, ce idn'est pas la clé primaire. Donc, même si vous n'êtes pas comptiez vous avez encore besoin de trouver tous les documents correspondants, peut - être des milliers d'entre eux. À propos de l'aliasing - le code est un travail constant en cours. Vous ne savez jamais quand vous devrez rentrer. Le crénelage permet d'éviter les erreurs d'exécution stupides; par exemple, un nom de colonne unique qui n'avait pas besoin d'alias n'est plus unique car quelqu'un a créé une colonne du même nom dans une autre table jointe.
Nikola Markovinović
Oui tu as absolument raison. Les alias aident beaucoup, mais je ne pense pas que cela fasse de différence lorsque vous n'utilisez pas de jointures. Alors, j'ai dit de ne pas l'utiliser si ce n'est pas nécessaire. :) Et vous pouvez trouver une longue discussion ici sur la vérification existence. :)
AgentSQL
3
Je ne sais pas pourquoi j'ai accepté le terme aliasing. Le terme correct est qualifying. Voici une explication plus longue d'Alex Kuznetzov . À propos des requêtes de table unique - il s'agit désormais d'une table unique . Mais plus tard, lorsque le bogue est découvert et que vous essayez de contenir l'inondation, le client est nerveux, vous rejoignez une autre table juste pour faire face au message d'erreur - message facilement corrigible, mais pas à ce moment de transpiration, un petit coup frappe - et vous corrigez le erreur de ne jamais quitter une colonne ...
Nikola Markovinović
1
Je ne peux pas ignorer ça maintenant. Merci!! :)
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

Cette approche renvoie un booléen pour vous.

Kris Coleman
la source
1
Peut probablement omettre l'instruction Top et l'instruction * pour la rendre un peu plus rapide, car Exist se fermera une fois qu'il aura trouvé un enregistrement, donc quelque chose comme ceci: SELECT CASE WHEN EXISTS (SELECT 1 FROM dbo. [YourTable] WHERE [YourColumn] = [YourValue]) THEN CAST (1 AS BIT) ELSE CAST (0 AS BIT) END
Stefan Zvonar
Cette suggestion ne mentionne pas pourquoi cela serait plus rapide par rapport aux instructions existantes / non existantes dans SQL Server. Sans aucune analyse comparative, j'aurais du mal à croire qu'une déclaration de cas donnerait un résultat plus rapide qu'une réponse vrai / faux immédiate.
Bonez024
8

Vous pouvez aussi utiliser

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

ELSE    
     BEGIN
       --<Do something>
     END
atik sarker
la source
7

Ne pensez pas que quiconque l'a encore mentionné, mais si vous êtes sûr que les données ne changeront pas en dessous de vous, vous pouvez également appliquer l'indice NoLock pour vous assurer qu'il n'est pas bloqué lors de la lecture.

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
la source
3
SELECT COUNT(*) FROM products WHERE products.id = ?;

Il s'agit de la solution de base de données relationnelle croisée qui fonctionne dans toutes les bases de données.

garçon voyou
la source
7
Cependant, vous forcez la base de données à boucler sur tous les enregistrements, très lente sur les grandes tables
amd
@amd veut expliquer pourquoi?
UmNyobe
@amd votre commentaire a tout son sens. Cette requête est plus un FIND ALL que FIND ANY.
UmNyobe
1

Vous trouverez ci-dessous le moyen le plus simple et le plus rapide de déterminer si un enregistrement existe ou non dans la base de données.La bonne chose est que cela fonctionne dans toutes les bases de données relationnelles

SELECT distinct 1 products.id FROM products WHERE products.id = ?;
manish Prasad
la source
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
la source
2
Peut-être que votre code fonctionne très bien, mais il serait préférable que vous ajoutiez des informations supplémentaires pour que cela soit mieux compréhensible.
idmean
0

Je l'ai utilisé dans le passé et il ne nécessite pas une analyse complète de la table pour voir si quelque chose existe. C'est super rapide ...

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

Pour ceux qui découvrent cela à partir de MySQL ou d'Oracle background - MySQL prend en charge la clause LIMIT pour sélectionner un nombre limité d'enregistrements, tandis qu'Oracle utilise ROWNUM.

Werner
la source