Posso fornecer um padrão para uma associação externa esquerda?

21

Suponha que eu tenha as tabelas a (com coluna a1) eb (com as colunas b1 e b2) e execute uma junção externa esquerda

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

Em seguida, b1 e b2 serão NULL onde um valor de a1 não tem valor correspondente de b1.

Posso fornecer um valor padrão para b2, em vez de NULL? Note-se que COALESCE não vai funcionar aqui, porque eu não quero que o valor padrão para substituir potenciais nulos em b2 onde não é um valor de a1 correspondência b1.

Ou seja, com aeb 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

e um padrão para b2 de, digamos, 100, quero obter o resultado

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

Nesse caso simples, eu poderia fazê-lo "manualmente" examinando se b1 é NULL na saída. Essa é a melhor opção em geral, ou existe uma maneira mais padrão e organizada?

Tom Ellis
fonte

Respostas:

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
fonte
2
Por favor, use ANSI SQL quando a pergunta for marcada apenas com sql(o que significa "SQL the query language". Essa tag não indica nenhum produto ou dialeto específico do DBMS). A parte: [b2]=CASE WHEN ... ENDé uma expressão SQL inválida (padrão).
precisa saber é o seguinte
Adicionei uma tag para indicar que aceitaria uma resposta específica do Postgres. Ainda, o SQL padrão seria preferido, se possível.
Tom Ellis
@ Kin: como indicado na minha pergunta, eu sei que "eu poderia fazê-lo" à mão ", verificando se b1 é NULL na saída. Essa é a melhor opção em geral, ou existe uma maneira mais padrão e organizada?"
Tom Ellis
3
como você deseja distinguir entre NULLs que ocorrem por causa de um JOIN e aqueles que estão "naturalmente" presentes, é inevitável que você tenha que examinar b1. Se é isso que você quis dizer com "eu poderia fazê-lo" à mão "", então sim, esse é o único caminho.
Mordechai
@MorDeror: OK, suponho que eu estivesse pensando que pode haver uma sintaxe como "LEFT OUTER JOIN ... ON ... DEFAULT b2 = ...".
Tom Ellis
2

A resposta original a esta pergunta foi inexplicável, então vamos dar uma outra chance.

Usando uma CASEdeclaração

Usando esse método, exploramos que temos outro valor em uma coluna diferente queIS NOT NULL , nesse caso, b.b1se esse valor for nulo, sabemos que a associação falhou.

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

Isso funcionará totalmente e gerará exatamente o que você deseja.

Usando um sub-SELECT

Não use esse método, é uma ideia de construção. Continue lendo.

Se não temos NOT NULLcolunas que possamos explorar dessa forma, precisamos de algo para criar uma coluna que possa funcionar dessa maneira para nós ...

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 uma comparação de linhas

Ainda mais fácil, ao forçar um valor falso com o qual podemos comparar, é comparar a linha. No PostgreSQL, a linha possui um valor pelo nome da tabela. Por exemplo, SELECT foo FROM fooretorna uma linha do tipo foo(que é um tipo de linha), da tabela foo. Aqui testamos para ver se esse ROW é nulo. Isso funcionará desde que todas as colunas IS NOT NULL. E, se todas as colunas IS NULLda sua tabela, você está apenas trollando.

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
fonte
A coluna b1usada na CASEsolução não precisa ser não anulável. A construção funciona em ambos os casos.
ypercubeᵀᴹ
1

Acho que o COALESCE é muito útil nesse caso. Ele retornará o primeiro valor não NULL de uma lista:

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