0
votes

Comment puis-je convertir une instruction NOT IN en une instruction NOT EXISTS dans SQL?

J'ai entendu dire que NOT IN devrait être évité à tout prix, donc je mets à jour mes requêtes pour utiliser NOT EXISTS et exécuter EXPLAIN ANALYZE sur eux pour voir s'il y a des améliorations, mais j'ai du mal à comprendre. Comment puis-je convertir une requête comme celle-ci:

SELECT id
FROM customers c
WHERE c.status NOT IN
    ('DELETED', 'ARCHIVED', 'EXPIRED'')


7 commentaires

"J'ai entendu dire que NOT IN devrait être évité à tout prix" - où avez-vous entendu cela? Je ne suis pas d'accord avec cette affirmation. N'oubliez pas que toutes les généralisations sont fausses, y compris celle-ci!


«À tout prix» était probablement une affirmation exagérée, j'aurais dû le formuler plus soigneusement. Il s'agissait plutôt d'un conseil de type "soyez prudent lorsque vous utilisez pas dans "


En fait, pour votre requête mentionnée dans la question NOT IN est assez bonne. la clause exist est meilleure lorsque vous utilisez une sous-requête dans la clause where pour vérifier l'existence d'une valeur


@AkhileshMishra la requête réelle est un peu plus encombrée dans les déclarations dans et pas dans mais je ne voulais pas publier l'original sans tout masquer, j'ai donc publié une version simplifiée


Je n'ai aucune connaissance de l'optimisation postgres mais IME avec d'autres produits, les problèmes avec NOT IN sont généralement associés à NOT IN (sub_query) surtout si la sub_query peut renvoyer < code> NULL .


Une réponse de ligne pour toutes vos requêtes est NOT IN est bonne lorsque vous avez un ensemble de valeurs statiques (comme en question) Mais NOT EXIST est toujours bonne lorsque vous voulez le vérifier via une sous-requête.


@AkhileshMishra C'est parfaitement logique, merci


3 Réponses :


1
votes

Cela peut être vrai si vous utilisez not in avec une sélection imbriquée, par exemple:

select id
from customer c
where not exists(
    select *
    from status s
    where (s.id, s.type) = (c.status_id, 'DELETED')
)

Cela devrait plutôt être réécrit en:

select id
from customer c
where c.status_id not in (
    select id
    from status s
    where s.type = 'DELETED'
)

Mais puisque vous utilisez not in avec un ensemble de valeurs possibles, c'est ok et il serait même difficile de le réécrire en utilisant existe .


3 commentaires

J'admets que je n'ai jamais vu la syntaxe where (s.id, s.type) = (c.status_id, 'DELETED') auparavant - est-ce unique à Postgres?


@Dai c'est un peu plus compact. C'est la même chose que s.id = c.status_id et s.type = 'DELETED'


@Dai oui, c'est un truc de postgres. Vous pouvez voter si vous avez appris quelque chose d'utile;>



0
votes

J'ai entendu dire que NOT IN devrait être évité à tout prix.

Vous avez mal entendu - et vous appliquez quelque chose que vous ne comprenez pas (ou quelque chose que vous acceptez pour argent comptant sans aucune enquête de votre part) en production. Ce que vous professez est un exemple de Programmation Cargo-Cult . Ne fais pas ça. Ne croyez rien de ce que vous lisez sans citations et sources (cela s'applique à tout dans la vie, btw)

Quoi qu'il en soit, NOT IN et NOT EXISTS sont pas sémantiquement équivalent: IN (et NOT IN ) lorsqu'il est utilisé avec des valeurs littérales variadiques ou des paramètres variadiques est juste du sucre syntaxique pour une instruction OR répétée , alors que NOT EXISTS est un test d'appartenance à l'ensemble, ce qui est une opération très différente.

Dans votre cas, ceci:

WHERE NOT ( c.status = 'DELETED' OR c.status = 'ARCHIVED' OR c.status = 'EXPIRED' )

0 commentaires

1
votes

Vous avez entendu la règle de manière incomplète. C'est faux:

J'ai entendu dire que NOT IN devrait être évité à tout prix,. . .

C'est beaucoup plus proche d'être vrai:

J'ai entendu dire que NOT IN avec une sous-requête devrait être évité à tout prix,. . .

Il y a deux raisons à cela. Le plus important est de loin la gestion des valeurs NULL . Si une valeur renvoyée par la sous-requête est NULL , alors NOT IN jamais renvoie TRUE . Autrement dit, la requête ne renvoie aucune ligne (si c'est la seule condition).

D'un autre côté, NOT EXISTS fait ce que vous attendez dans ce cas, en ignorant essentiellement les valeurs NULL dans la sous-requête.

Ce n'est pas un problème avec les listes explicites, car il est peu probable que vous incluiez une valeur NULL dans une liste explicite.

Le deuxième problème concerne les performances. Certaines bases de données optimiseront NOT EXISTS avec une sous-requête bien meilleure que NOT IN - en particulier si les index appropriés sont disponibles.


0 commentaires