web-dev-qa-db-fra.com

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

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?

13
Mark Warner

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.id Pour 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. Trouver une telle valeur "magique" pour les nombres pourrait être possible, mais ce sera très difficile pour les valeurs de caractères (surtout parce qu'Oracle traite une chaîne vide comme null également)

De plus: aucun index sur les colonnes id ne sera utilisé (vous pourriez définir un index basé sur une fonction avec la coalesce() expression cependant).

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)
31

Alternativement, vous pouvez faire correspondre deux valeurs nulles en utilisant INTERSECT comme 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 qu'il ne soit en réalité pas beaucoup plus long que suggestion de BriteSponge . Cependant, ce n'est certainement pas un match, si vous pardonnez le jeu de mots, à la concision de ce qui est mentionné précédemment dans les commentaires de manière standard, qui est le IS NOT DISTINCT FROM opérateur, non encore pris en charge dans Oracle.

6
Andriy M

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

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

decode traite 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)

2
Tamás Bárász

Pour être complet, je mentionnerai que la fonction SYS_OP_MAP_NONNULL peut désormais être utilisé en toute sécurité pour comparer des valeurs nulles, comme cela est désormais documenté dans la documentation 12c. Cela signifie qu'Oracle ne le supprimera pas 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 à Vues matérialisées de base - Choix d'index pour les vues matérialisées .

2
BriteSponge

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

Voilà pourquoi nous avons IS NULL/IS NOT NULL pour 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.

1
Andrew Puglionesi