web-dev-qa-db-fra.com

SQL "Join" sur les valeurs nulles

Pour des raisons indépendantes de ma volonté, j'ai besoin de joindre deux tables et j'ai besoin de valeurs nulles pour correspondre. La meilleure option à laquelle je pouvais penser était de cracher un UUID et de l'utiliser comme valeur de comparaison, mais cela semble moche

SELECT * FROM T1 JOIN T2 ON nvl(T1.SOMECOL,'f44087d5935dccbda23f71f3e9beb491') = 
   nvl(T2.SOMECOL,'f44087d5935dccbda23f71f3e9beb491')

Comment puis-je faire mieux? C'est sur Oracle si cela importe, et le contexte est une application dans laquelle un lot de données téléchargées par l'utilisateur doit être comparé à un lot de données existantes pour voir si des lignes correspondent. Rétrospectivement, nous aurions dû empêcher l'une des colonnes de jointure des deux ensembles de données de contenir des valeurs nulles, mais nous ne l'avons pas fait et nous devons maintenant vivre avec.

Edit: Pour être clair, je ne suis pas seulement préoccupé par les null. Si les colonnes ne sont pas nulles, je veux qu'elles correspondent à leurs valeurs réelles.

23
Dan

Peut-être que cela fonctionnerait, mais je ne l'ai jamais essayé:

SELECT * 
FROM T1 JOIN T2 
ON T1.SOMECOL = T2.SOMECOL OR (T1.SOMECOL IS NULL AND T2.SOMECOL IS NULL)
43
Eric Petroelje

Dans SQL Server, j'ai utilisé:

WHERE (a.col = b.col OR COALESCE(a.col, b.col) IS NULL)

Évidemment pas efficace, à cause de l'OR, mais à moins qu'il n'y ait une valeur réservée, vous pouvez mapper des NULL des deux côtés sans ambiguïté ou pliage, c'est à peu près le mieux que vous puissiez faire (et s'il y en avait, pourquoi NULL était-il même autorisé dans votre conception ... .)

6
Cade Roux

Pour ce type de tâche, Oracle utilise en interne une fonction non documentée sys_op_map_nonnull (), où votre requête deviendrait:

SELECT *
FROM T1 JOIN T2 ON sys_op_map_nonnull(T1.SOMECOL) = sys_op_map_nonnull(T2.SOMECOL)

Sans papiers, alors soyez prudent si vous suivez cette voie.

3
David Aldridge

Vous ne pouvez pas faire mieux, mais le JOIN que vous avez ne fera en aucun cas un "JOIN" réel (il n'y aura aucune corrélation entre T1.SOMECOL et T2.SOMECOL à part qu'ils ont tous deux une valeur NULL pour cela colonne). Fondamentalement, cela signifie que vous ne pourrez pas utiliser un JOIN sur NULL pour voir si les lignes correspondent.

NULL n'est jamais égal à un autre NULL. Comment quelque chose de valeur inconnue peut-il être égal à quelque chose d'autre de valeur inconnue?

3
Ken White

Simple, utilisez COALESCE, qui renverra son premier paramètre non nul:

SELECT * FROM T1 JOIN T2 ON 
  COALESCE(T1.Field, 'magic string') = 
     COALESCE(T2.Field, 'magic string')

La seule chose dont vous aurez à vous soucier est que la "chaîne magique" ne peut pas être parmi les valeurs légales pour le champ de jointure dans les deux tableaux.

3
Michael Hays

Voulez-vous vraiment pouvoir rejoindre les tables si une valeur est nulle? Ne pouvez-vous pas simplement exclure les valeurs nulles possibles dans le prédicat de jointure? J'ai du mal à comprendre que les lignes de deux tables peuvent être liées par une valeur nulle. Si vous avez 100 null dans table1.col_a et 100 null dans table2.col_b, vous allez avoir 10000 lignes renvoyées juste pour les lignes avec null. Cela semble incorrect.

Cependant, vous avez dit que vous en aviez besoin. Puis-je suggérer de fusionner la colonne nulle en une chaîne plus petite car les comparaisons de caractères sont relativement coûteuses. Encore mieux, fusionnez les valeurs nulles en un entier si les données dans les colonnes vont être du texte. Ensuite, vous avez des "comparaisons" très rapides et il est peu probable que vous entriez en collision avec des données existantes.

1
Josh Smeaton

Je pense que vous pouvez toujours utiliser nvl () pour rejoindre:

SELECT *
FROM T1
JOIN T2 ON NVL(T2.COL1,-1)=NVL(T1.COL1,-1);

Mais vous devrez ajouter des index basés sur les fonctions sur les colonnes col1

CREATE INDEX IND_1 ON T1 (NVL(COL1,-1));
CREATE INDEX IND_2 ON T2 (NVL(COL1,-1));

Les index devraient améliorer considérablement la vitesse de la jointure sur NVL (..).

0
Cloud

Il suffit de lancer ceci - existe-t-il un moyen de fusionner ces valeurs nulles en une valeur connue, comme une chaîne vide? Ne pas savoir grand-chose sur la disposition de votre table signifie que je ne peux pas être sûr que vous perdrez le sens de cette façon - c'est-à-dire que la chaîne vide représente "l'utilisateur a refusé d'entrer un numéro de téléphone" et NULL étant "nous avons oublié à poser des questions à ce sujet ", ou quelque chose comme ça?

Il y a des chances que ce ne soit pas possible, j'en suis sûr, mais si c'est le cas, vous aurez des valeurs connues à comparer et vous pourrez obtenir une jointure légitime de cette façon.

0
Jim Dagg

Pourquoi pas quelque chose comme ça:

SELECT * FROM T1 JOIN T2 ON nvl (T1.SOMECOL, 'null') = nvl (T2.SOMECOL, 'null')

Je ne sais pas pourquoi vous utilisez l'UUID. Vous pouvez utiliser n'importe quelle chaîne non présente dans les colonnes, comme la chaîne "null", par exemple, pour réduire l'empreinte mémoire. Et la solution utilisant nvl est beaucoup plus rapide que la solution utilisant or ... is null proposé par Eric Petroelje, par exemple.

0
Alexis Dufrenoy

N'est-ce pas la même chose que de vérifier la présence de null dans les deux colonnes?

SELECT * FROM T1, T2 WHERE T1.SOMECOL IS NULL and T2.SOMECOL IS NULL

ou

SELECT * FROM T1 CROSS JOIN T2 WHERE T1.SOMECOL IS NULL and T2.SOMECOL IS NULL
0
Gambler

@Sarath Avanavu

Celui-ci n'est pas la meilleure approche. Si TA.COL1 conserve la valeur 0 et TB.COL2 est NULL, il joindra ces enregistrements, ce qui n'est pas correct.

SELECT *
FROM TABLEA TA
JOIN TABLEB TB ON NVL(TA.COL1,0)=NVL(TB.COL2,0);
0
Andrey Batyrenko

Vous pouvez joindre des valeurs nulles en utilisant decode:

    SELECT * FROM T1 JOIN T2 ON DECODE(T1.SOMECOL, T2.SOMECOL, 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)

0
Tamás Bárász

Vous pouvez essayer d'utiliser la requête ci-dessous.

SELECT *
FROM TABLEA TA
JOIN TABLEB TB ON NVL(TA.COL1,0)=NVL(TB.COL2,0);
0
Sarath Avanavu