Por que não consigo usar valores nulos nas junções?

13

Eu resolvi o problema de consulta usando ... row_number() over (partition by... essa é uma pergunta mais geral sobre por que não podemos usar colunas com valores nulos em junções. Por que um nulo não pode ser igual a um nulo por causa de uma junção?

Mark Warner
fonte

Respostas:

31

Por que um nulo não pode ser igual a um nulo por causa de uma junção?

Apenas diga à Oracle para fazer isso:

select *
from one t1 
  join two t2 on coalesce(t1.id, -1) = coalesce(t2.id, -1);

(Observe que no SQL padrão você pode usar t1.id is not distinct from t2.idpara obter um operador de igualdade com segurança nula, mas o Oracle não suporta isso)

Mas isso só funcionará se o valor de substituição (-1 no exemplo acima) não aparecer realmente na tabela. Encontrar um valor "mágico" para números pode ser possível, mas será muito difícil para os valores dos caracteres (especialmente porque o Oracle também trata uma string vazia null)

Mais: nenhum índice nas idcolunas será usado (você pode definir um índice baseado em função com a coalesce()expressão).

Outra opção que funciona para todos os tipos, sem valores mágicos:

              on t1.id = t2.id or (t1.id is null and t2.id is null)

Mas a verdadeira questão é: isso faz sentido?

Considere os seguintes dados de exemplo:

Quadro um

id
----
1
2
(null)
(null)

Quadro dois

id
----
1
2
(null)
(null)
(null)

Qual combinação de valores nulos deve ser escolhida na junção? Meu exemplo acima resultará em algo como uma junção cruzada para todos os valores nulos.

T1_ID  | T2_ID 
-------+-------
     1 |      1
     2 |      2
(null) | (null)
(null) | (null)
(null) | (null)
(null) | (null)
(null) | (null)
(null) | (null)
um cavalo sem nome
fonte
6

Como alternativa, você pode fazer com que dois nulos se correspondam usando INTERSECTcomo um operador de igualdade:

SELECT
  *
FROM
  t1
  INNER JOIN t2
    ON EXISTS (SELECT t1.ID FROM DUAL INTERSECT SELECT t2.ID FROM DUAL)
;

Veja esta demonstração do DBFiddle para uma ilustração.

Obviamente, isso parece um bocado, embora na verdade não demore muito mais do que a sugestão de BriteSponge . No entanto, certamente não é uma correspondência, se você perdoa o trocadilho, com a concisão do mencionado anteriormente nos comentários, de maneira padrão, que é o IS NOT DISTINCT FROMoperador, ainda não suportado no Oracle.

Andriy M
fonte
2

Apenas para completar, vou mencionar que a função SYS_OP_MAP_NONNULLagora pode ser usada com segurança para comparar valores nulos, como agora está documentado na documentação 12c. Isso significa que o Oracle não o remove aleatoriamente e quebra seu código.

SELECT *
FROM   one t1 
       JOIN two t2
         ON SYS_OP_MAP_NONNULL(t1.id) = SYS_OP_MAP_NONNULL(t2.id)

A vantagem é que você não encontra o problema numérico "mágico".

A referência nos documentos do Oracle está em Vistas materializadas básicas - Escolha de índices para exibições materializadas .

BriteSponge
fonte
Então está documentado agora? Porque o AskTom (em 2003) declarou: " - não é documentado e, portanto, representa um risco de desaparecer ou alterar a funcionalidade, o que é suficiente para que as pessoas simplesmente" parem de ler "vão lá e você pode estar realmente louco no próximo lançamento. a única maneira CORRETA é: ponto where (a = b or (a is null and b is null)) final. são os meus pensamentos sobre isso. Eu não consideraria usar sys_op_map_nonnull, ignoraria aquele homem atrás da cortina. "
precisa saber é o seguinte
Se você possui um link, adicione-o à pergunta. Não encontrei menção no 12c Functions, mas é difícil pesquisar a documentação e a versão específica do Oracle.
precisa saber é o seguinte
2

Você pode associar valores nulos usando decodificação:

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

decodetrata nulos como iguais, portanto, isso funciona sem números "mágicos". As duas colunas devem ter o mesmo tipo de dados.

Não criará o código mais legível, mas provavelmente ainda melhor do que t1.id = t2.id or (t1.id is null and t2.id is null)

Tamás Bárász
fonte
1

Por que você não pode usar valores nulos em junções? No Oracle, os seguintes itens não são avaliados como verdadeiros:

  • NULL = NULL
  • NULL <> NULL

É por isso que temos IS NULL/ IS NOT NULLpara verificar valores nulos.
Para testar isso, você pode simplesmente fazer:

SELECT * FROM table_name WHERE NULL = NULL

As junções estão avaliando uma condição booleana e não as programaram para operar de maneira diferente. Você pode colocar um sinal de maior que na condição de junção e adicionar outras condições; apenas avalia como uma expressão booleana.

Eu acho que um nulo não pode ser igual a um nulo em junções por uma questão de consistência. Isso desafiaria o comportamento usual do operador de comparação.

Andrew Puglionesi
fonte
NULL = anythingresulta NULLporque o padrão SQL diz isso. Uma linha satisfaz a condição de junção apenas se a expressão for verdadeira.
Laurenz Albe
1
Além dos detalhes da implementação literal (o que nem sempre é o caso: alguns bancos de dados têm a opção de igualar NULL a NULL para alguns / todos os fins), existe um motivo lógico: NULL é desconhecido. Quando você compara NULL a NULL, está perguntando "essa coisa desconhecida é igual a outra coisa desconhecida", para a qual a única resposta razoável é "desconhecida" - outro NULL (que é mapeado como falso em uma situação de comparação).
David Spillett
-4

Um valor nulo na maioria dos bancos de dados relacionais é considerado DESCONHECIDO. Não deve ser confundido com todos os zeros HEX. se algo contiver nulo (desconhecido), você não poderá compará-lo.

Unknown = Known False
Unknown = Unknown False
Unknown >= Known False
Known >= Unknown False

O que significa que, sempre que você tiver um nulo como operando em uma expressão booleana, a parte else será sempre verdadeira.

Ao contrário do ódio geral contra o nulo pelos desenvolvedores, o nulo tem seu lugar. Se algo for desconhecido, use null.

jujiro
fonte
6
Na verdade, todos os exemplos de comparações que você tem, de rendimento UNKNOWN, não FALSE;)
ypercubeᵀᴹ
Você está certo, no entanto, o objetivo de uma expressão booleana é resultar apenas verdadeiro ou falso, portanto, não vamos ficar loucos aqui :).
Jujiro