Puis-je fournir une valeur par défaut pour une jointure externe gauche?

21

Supposons que j'ai des tables a (avec la colonne a1) et b (avec les colonnes b1 et b2) et que j'effectue une jointure externe gauche

SELECT *
FROM a LEFT OUTER JOIN b
ON a.a1 = b.b1

Alors b1 et b2 seront NULL où une valeur de a1 n'a pas de valeur correspondante de b1.

Puis-je fournir une valeur par défaut pour b2, au lieu de NULL? Notez que COALESCE ne fonctionnera pas ici, car je ne veux pas que la valeur par défaut remplace les NULL potentiels dans b2 où il y a une valeur de b1 correspondant à a1.

Autrement dit, avec a et b comme

CREATE TABLE a (a1)
  AS VALUES (1),
            (2),
            (3) ;

CREATE TABLE b (b1,b2)
  AS VALUES (1, 10),
            (3, null) ;


a1     b1 | b2
---    --------
 1      1 | 10
 2      3 | NULL
 3

et une valeur par défaut pour b2 de, disons 100, je veux obtenir le résultat

a1 | b1   | b2
---------------
1  |  1   | 10
2  | NULL | 100
3  |  3   | NULL

Dans ce cas simple, je pourrais le faire "à la main" en regardant si b1 est NULL dans la sortie. Est-ce la meilleure option en général, ou existe-t-il une manière plus standard et plus soignée?

Tom Ellis
la source

Réponses:

23
SELECT a.a1,b.b1,  
    CASE WHEN b.b1 is NULL THEN 5 ELSE b.b2 END AS b2  
FROM a LEFT OUTER JOIN b  
ON a.a1 = b.b1
Mordechai
la source
2
Veuillez utiliser ANSI SQL lorsque la question est uniquement balisée sql(ce qui signifie "SQL le langage de requête". Cette balise ne désigne aucun produit SGBD ou dialecte spécifique). La partie: [b2]=CASE WHEN ... ENDest une expression SQL non valide (standard).
a_horse_with_no_name
J'ai ajouté une balise pour indiquer que j'accepterais une réponse spécifique à Postgres. Néanmoins, le SQL standard serait préférable si possible.
Tom Ellis
@Kin: comme indiqué dans ma question, je sais que "je pourrais le faire" à la main "en regardant si b1 est NULL dans la sortie. Est-ce la meilleure option en général, ou y a-t-il une manière plus standard et plus soignée?"
Tom Ellis
3
comme vous voulez faire la distinction entre les valeurs NULL qui se produisent en raison d'un JOIN et celles qui sont "naturellement" présentes, il est inévitable que vous deviez examiner b1. Si c'est ce que vous vouliez dire par "je pourrais le faire" à la main "", alors oui, c'est le seul moyen.
Mordechai
@MorDeror: OK, je suppose que je pensais qu'il pourrait y avoir une syntaxe telle que "LEFT OUTER JOIN ... ON ... DEFAULT b2 = ...".
Tom Ellis
2

La réponse originale à cette question est restée inexpliquée, nous allons donc donner un autre coup.

Utiliser une CASEdéclaration

En utilisant cette méthode, nous exploitons que nous avons une autre valeur dans une autre colonne queIS NOT NULL dans ce cas, b.b1si cette valeur est nulle, nous savons que la jointure a échoué.

SELECT
  a.a1,
  b.b1,  
  CASE WHEN b.b1 is NULL THEN 100 ELSE b.b2 END AS b2  
FROM a
LEFT OUTER JOIN b  
  ON (a.a1 = b.b1);

Cela fonctionnera totalement et générera exactement ce que vous voulez.

Utilisation d'un sous-SELECT

N'utilisez pas cette méthode, c'est une idée d'accumulation. Continue de lire.

Si nous n'avons pas de NOT NULLcolonnes que nous pouvons exploiter comme ça, nous avons besoin de quelque chose pour créer une colonne qui puisse fonctionner de cette façon pour nous ...

SELECT
  a.a1,
  b.b1,  
  CASE WHEN b.cond IS NULL THEN 100 ELSE b.b2 END AS b2  
FROM a
LEFT OUTER JOIN (
  SELECT true AS cond, b.*
  FROM b
) AS b
  ON (a.a1 = b.b1);

Utilisation d'une comparaison de lignes

Il est encore plus facile de forcer une valeur fausse pour laquelle nous pouvons comparer, c'est de comparer la ligne. Dans PostgreSQL, la ligne a une valeur par le nom de la table. Par exemple, SELECT foo FROM fooretourne une ligne de type foo(qui est un type de ligne), à ​​partir de la table foo. Ici, nous testons pour voir si cette ROW est nulle. Cela fonctionnera tant que chaque colonne IS NOT NULL. Et, si chaque colonne IS NULLde votre table, alors vous êtes juste à la traîne.

SELECT
  a.a1,
  b.b1,  
  CASE WHEN b IS NULL THEN 100 ELSE b.b2 END AS b2  
FROM a
LEFT OUTER JOIN b
  ON (a.a1 = b.b1);
Evan Carroll
la source
La colonne b1utilisée dans la CASEsolution n'a pas besoin d'être non nulle. La construction fonctionne dans les deux cas.
ypercubeᵀᴹ
1

Je trouve COALESCE très utile dans ce cas. Il renverra la première valeur non NULL d'une liste:

SELECT
 a.a1,
 b.b1,
 COALESCE (b.b2, 100) AS b2
FROM a
LEFT OUTER JOIN b
  ON (a.a1 = b.b1);
Rob
la source