Clause SQL Server Three-Valued Logic et NOT IN avec valeurs NULL

Tout en interrogeant le serveur de production pour résoudre un problème critique, une requête a tout à coup attiré mon attention. Nous avions écrit une requête qui a été créée pour rechercher des enregistrements qui existent dans la table A mais qui n'existent pas dans la table B, basé sur une certaine colonne. La requête était syntaxiquement correcte et s’exécutait sans erreur, mais elle n’a donné aucun résultat. Nous espérions obtenir des résultats dans certains enregistrements. Nous avons donc cherché à savoir pourquoi la requête correcte n'en renvoyait aucun.

le PAS DEDANS La clause renvoie les lignes de la table externe qui n'existent pas dans la table interne utilisée dans la sous-requête. Dans ce tutoriel, nous examinerons l'utilisation de la clause NOT IN avec des valeurs null.

Exemple de clause NOT IN

SELECT * FROM OuterTable WHERE PK_Id NOT IN (SELECT FK_id à partir d'InnerTable); 

Exemple de clause NOT IN avec des valeurs NULL dans InnerTable.

CREATE TABLE PRODUCT (PK_Product_Id INT, CLÉ PRIMAIRE, nom VARCHAR (255)); INSÉRER DANS LES VALEURS DU PRODUIT (1, 'Coke'), (2, 'Pepsi'), (3, 'Mangue'), (4, '7 Up'); CREATE TABLE PRODUCT_DETAILS (PK_Details_Id INT, CLÉ PRIMAIRE, [Description] VARCHAR (500), FK_Product_Id INT PRODUIT DE RÉFÉRENCES DE CLÉS ÉTRANGÈRE (PK_Product_Id)); INSERT IN PRODUCT_DETAILS VALUES (100, «500 ML c'est bien», 1); INSERT IN PRODUCT_DETAILS VALUES (101, «500 ML, c'est bien», 2); INSERT IN PRODUCT_DETAILS VALUES (102, «500 ML, c'est bien», 3); INSERT IN PRODUCT_DETAILS VALUES (103, "500 ML, c'est bon", NULL); 

Comme le montre l'image ci-dessus, la table PRODUCT_DETAILS en contient une. Valeur NULL.

Maintenant, notre objectif est pour trouver les noms des produits de la table Product dont les détails ne sont pas disponibles dans la table Product_Details. Idéalement, le produit 4 doit être renvoyé de la table Product car les détails du produit 4 n'existent pas dans la table Product_Details.

Nous pourrions penser que cela peut être facilement réalisé en utilisant le prédicat NOT IN en écrivant la requête suivante.

SELECT * FROM PRODUCT WHERE PK_Product_Id NOT IN (SÉLECTIONNER Fk_Product_Id FROM PRODUCT_DETAILS); 

La requête ci-dessus ne renvoie rien, bien que sa syntaxe soit correcte. En raison de l'existence d'une valeur NULL dans la table Product_Details, il ne parvient pas à renvoyer les résultats attendus.

SQL Server utilise le Logique à trois valeurs concept ici.

Comme nous le savons, une valeur NULL existe dans la table Product_Details de la colonne fk_product_id. UNE La valeur NULL est une valeur inconnue ou manquante.

SQL Server convertit la clause NOT IN à l'aide d'une logique à trois valeurs et l'évalue de la manière suivante.

NOT IN (SELECT 1 OU 2 OU 3 OU NULL) NOT IN (Fk_product_id = 1 OU Fk_product_id = 2 OU Fk_product_id = 3 OU Fk_product_id = NULL) PAS IN (Fk_product_id = 1 OU Fk_product_id = 1 OU Fk_product_id = 2 ou Fk_product_id.). (VRAI OU VRAI OU VRAI OU INCONNU) PAS EN (VRAI OU VRAI OU INCONNU) PAS EN (VRAI OU INCONNU) PAS EN (INCONNU) 

Comme le le résultat final est évalué en tant que UNKNOWN, la requête NOT IN ne renvoie aucun résultat en raison de l'existence d'une valeur NULL.

La solution consiste à faire fonctionner les requêtes NOT IN avec l'existence de valeurs NULL et à utiliser une logique à deux valeurs, uniquement TRUE ou FALSE.

--NOT IN WITH IS NOT NULL Filtre SÉLECTION * DE PRODUIT WHERE PK_Product_Id NOT IN (SÉLECTIONNEZ Fk_Product_Id DE PRODUCT_DETAILS WHERE FK_Product_Id N'EST PAS NULL); --NOT Exists SELECT * FROM Produit Prd WHERE NOT EXISTS (SELECT Fk_Product_Id FROM PRODUCT_DETAILS WHERE Fk_Product_Id = Prd.PK_Product_Id); 

NOT EXISTS nous donne également les bons résultats car il utilise la logique booléenne à deux valeurs, uniquement TRUE ou False, pour filtrer les lignes.

--Using Left Join SELECT * FROM PRODUIT PRD LEFT JOIN PRODUCT_DETAILS PrdDetails ON Prd. PK_Product_Id = PrdDetails .FK_Product_Id WHERE PrdDetails. FK_Product_Id EST NULL 

À l'aide de la jointure gauche, nous pouvons également récupérer des enregistrements qui existent dans la table Product mais qui n'existent pas dans la page Product_Details lorsque nous faisons une jointure basée sur la clé primaire (pk_product_id) et la clé étrangère (fk_product_id).