Pourquoi ne puis-je pas utiliser de valeurs nulles dans les jointures?

13

J'ai résolu le problème de requête en utilisant ... row_number() over (partition by... c'est une question plus générale sur la raison pour laquelle nous ne pouvons pas utiliser des colonnes avec des valeurs nulles dans les jointures. Pourquoi un null ne peut-il pas être égal à un null pour le bien d'une jointure?

Mark Warner
la source

Réponses:

31

Pourquoi un null ne peut-il pas être égal à un null pour le bien d'une jointure?

Dites simplement à Oracle de le faire:

select *
from one t1 
  join two t2 on coalesce(t1.id, -1) = coalesce(t2.id, -1);

(Notez que dans SQL standard, vous pouvez utiliser t1.id is not distinct from t2.idpour obtenir un opérateur d'égalité nul, mais Oracle ne le prend pas en charge)

Mais cela ne fonctionnera que si la valeur de remplacement (-1 dans l'exemple ci-dessus) n'apparaît pas réellement dans le tableau. Il peut être possible de trouver une telle valeur "magique" pour les nombres , mais ce sera très difficile pour les valeurs de caractère (en particulier parce qu'Oracle traite également une chaîne vide null)

Plus: aucun index sur les idcolonnes ne sera utilisé (vous pouvez cependant définir un index basé sur une fonction avec l' coalesce()expression).

Une autre option qui fonctionne pour tous les types, sans valeurs magiques:

              on t1.id = t2.id or (t1.id is null and t2.id is null)

Mais la vraie question est: est -ce que cela a du sens?

Tenez compte des exemples de données suivants:

Tableau un

id
----
1
2
(null)
(null)

Tableau deux

id
----
1
2
(null)
(null)
(null)

Laquelle des combinaisons de valeurs nulles doit être choisie dans la jointure? Mon exemple ci-dessus se traduira par quelque chose comme une jointure croisée pour toutes les valeurs nulles.

T1_ID  | T2_ID 
-------+-------
     1 |      1
     2 |      2
(null) | (null)
(null) | (null)
(null) | (null)
(null) | (null)
(null) | (null)
(null) | (null)
un cheval sans nom
la source
6

Alternativement, vous pouvez faire correspondre deux valeurs nulles en utilisant INTERSECTcomme opérateur d'égalité:

SELECT
  *
FROM
  t1
  INNER JOIN t2
    ON EXISTS (SELECT t1.ID FROM DUAL INTERSECT SELECT t2.ID FROM DUAL)
;

Voir cette démo DBFiddle pour une illustration.

Bien sûr, cela semble être une bouchée, bien que ce ne soit pas beaucoup plus long que la suggestion de BriteSponge . Cependant, ce n'est certainement pas une correspondance, si vous pardonnez le jeu de mots, à la concision de la manière standard mentionnée précédemment, qui est l' IS NOT DISTINCT FROMopérateur, qui n'est pas encore pris en charge dans Oracle.

Andriy M
la source
2

Juste pour être complet, je mentionnerai que la fonction SYS_OP_MAP_NONNULLpeut maintenant être utilisée en toute sécurité pour comparer des valeurs nulles car elle est maintenant documentée dans la documentation 12c. Cela signifie qu'Oracle ne le supprimera pas simplement au hasard et ne cassera pas votre code.

SELECT *
FROM   one t1 
       JOIN two t2
         ON SYS_OP_MAP_NONNULL(t1.id) = SYS_OP_MAP_NONNULL(t2.id)

L'avantage étant que vous ne rencontrez pas le problème des nombres «magiques».

La référence dans les documents Oracle se trouve dans Vues matérialisées de base - Choix d'index pour les vues matérialisées .

BriteSponge
la source
Donc, c'est documenté maintenant? Parce que AskTom (en 2003) a déclaré: " - il n'est pas documenté, et présente donc un risque de s'en aller ou de changer les fonctionnalités, ce qui est assez dit pour que les gens" arrêtent de lire "puissent y aller et vous pourriez être vraiment fou dans la prochaine version. la seule façon CORRECTE est: where (a = b or (a is null and b is null)) point. c'est ce que j'en pense. Je n'envisagerais pas d'utiliser sys_op_map_nonnull, d'ignorer cet homme derrière le rideau. "
ypercubeᵀᴹ
Si vous avez un lien, veuillez l'ajouter à la question. Je n'ai pas trouvé de mention dans les fonctions 12c mais la recherche de documentation Oracle et de version spécifique est plutôt difficile.
ypercubeᵀᴹ
2

Vous pouvez joindre des valeurs nulles en utilisant le décodage:

on decode(t1.id, t2.id, 1, 0) = 1

decodetraite les valeurs nulles comme égales, donc cela fonctionne sans nombres "magiques". Les deux colonnes doivent avoir le même type de données.

Il ne rendra pas le code le plus lisible, mais probablement encore meilleur que t1.id = t2.id or (t1.id is null and t2.id is null)

Tamás Bárász
la source
1

Pourquoi ne pouvez-vous pas utiliser des valeurs nulles dans les jointures? Dans Oracle, les deux éléments suivants ne sont pas évalués comme vrais:

  • NULL = NULL
  • NULL <> NULL

C'est pourquoi nous devons IS NULL/ IS NOT NULLpour vérifier les valeurs nulles.
Pour tester cela, vous pouvez simplement faire:

SELECT * FROM table_name WHERE NULL = NULL

Les jointures évaluent une condition booléenne et ne les ont pas programmées pour fonctionner différemment. Vous pouvez mettre un signe supérieur à dans la condition de jointure et ajouter d'autres conditions; il l'évalue simplement comme une expression booléenne.

Je suppose qu'un null ne peut pas être égal à un null dans les jointures par souci de cohérence. Cela défierait le comportement habituel de l'opérateur de comparaison.

Andrew Puglionesi
la source
NULL = anythingrésulte NULLparce que la norme SQL le dit. Une ligne ne satisfait à la condition de jointure que si l'expression est vraie.
Laurenz Albe
1
Au-delà du détail de l'implémentation littérale (ce qui n'est pas toujours le cas: certaines bases de données ont la possibilité d'assimiler NULL à NULL à certaines fins), il y a une raison logique: NULL est inconnu. Lorsque vous comparez NULL à NULL, vous demandez "cette chose inconnue est-elle égale à cette autre chose inconnue" à laquelle la seule réponse raisonnable est "inconnu" - un autre NULL (qui est mappé sur faux dans une situation de comparaison).
David Spillett
-4

Une valeur nulle dans la plupart des bases de données relationnelles est considérée comme INCONNUE. Il ne doit pas être confondu avec tous les zéros HEX. si quelque chose contient null (inconnu), vous ne pouvez pas le comparer.

Unknown = Known False
Unknown = Unknown False
Unknown >= Known False
Known >= Unknown False

Ce qui signifie que chaque fois que vous avez un null comme opérande dans une expression booléenne, la partie else sera toujours vraie.

Contrairement à la haine générale envers null par les développeurs, null a sa place. Si quelque chose est inconnu, utilisez null.

jujiro
la source
6
En fait, tous les exemples de comparaisons que vous avez, cèdent UNKNOWN, non FALSE;)
ypercubeᵀᴹ
Vous avez raison, cependant le but d'une expression booléenne est de produire un résultat vrai ou faux uniquement, alors, ne devenons pas fous ici :).
jujiro