¿Puedo proporcionar un valor predeterminado para una combinación externa izquierda?

21

Supongamos que tengo tablas a (con la columna a1) yb (con las columnas b1 y b2) y realizo una unión externa izquierda

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

Entonces b1 y b2 serán NULL donde un valor de a1 no tiene un valor coincidente de b1.

¿Puedo proporcionar un valor predeterminado para b2, en lugar de NULL? Nota que se unen no funcionará aquí, porque no quiero que el valor por defecto para anular los nulos potenciales en b2 donde no es un valor de a1 b1 juego.

Es decir, con a y b como

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

y un valor predeterminado para b2 de, digamos, 100, quiero obtener el resultado

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

En este caso simple, podría hacerlo "a mano" mirando si b1 es NULL en la salida. ¿Es esa la mejor opción en general, o hay una forma más estándar y ordenada?

Tom Ellis
fuente

Respuestas:

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
fuente
2
Utilice ANSI SQL cuando la pregunta solo esté etiquetada sql(lo que significa "SQL, el lenguaje de consulta". Esa etiqueta no denota ningún producto o dialecto DBMS específico). La parte: [b2]=CASE WHEN ... ENDes una expresión SQL no válida (estándar).
a_horse_with_no_name
Agregué una etiqueta para indicar que aceptaría una respuesta específica de Postgres. Aún así, se preferiría SQL estándar si es posible.
Tom Ellis
@Kin: como dije en mi pregunta, sé que "podría hacerlo" a mano "mirando si b1 es NULL en la salida. ¿Es esa la mejor opción en general, o hay una forma más estándar y ordenada?"
Tom Ellis
3
dado que desea distinguir entre NULL que ocurren debido a un JOIN y aquellos que están "naturalmente" presentes, es inevitable que tenga que examinar b1. Si eso es lo que quisiste decir con "podría hacerlo" a mano "", entonces sí, esa es la única manera.
Mordechai
@MorDeror: OK, supongo que estaba pensando que podría haber una sintaxis como "IZQUIERDA EXTERIOR UNIRSE ... ENCENDIDO ... POR DEFECTO b2 = ...".
Tom Ellis
2

La respuesta original a esta pregunta no se explicó, así que demos otra oportunidad.

Usando una CASEdeclaración

Usando este método, aprovechamos que tenemos otro valor en una columna diferente que,IS NOT NULL en este caso, b.b1si ese valor es nulo, sabemos que la unión falló.

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

Esto funcionará totalmente y generará exactamente lo que desea.

Usando un sub-SELECT

No use este método, es una idea de acumulación. Sigue leyendo.

Si no tenemos NOT NULLcolumnas que podamos explotar de esa manera, necesitamos algo para crear una columna que pueda funcionar de esa manera para nosotros ...

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

Usando una comparación de filas

Sin embargo, incluso más fácil que forzar un valor falso para el que podemos comparar, es comparar la fila. En PostgreSQL, la fila tiene un valor por el nombre de la tabla. Por ejemplo, SELECT foo FROM foodevuelve una fila de tipo foo(que es un tipo de fila), de la tabla foo. Aquí probamos para ver si esa FILA es nula. Esto funcionará siempre que cada columna IS NOT NULL. Y, si cada columna IS NULLde tu tabla, entonces solo estás trolleando.

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
fuente
La columna b1utilizada en la CASEsolución no necesita ser no anulable. La construcción funciona en cualquier caso.
ypercubeᵀᴹ
1

Creo que COALESCE es muy útil en ese caso. Devolverá el primer valor no NULL de una lista:

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