Similaridades entre NA no R e NULL no PostgreSQL

Neste tuturial iremos mostrar como o comportamento de NAs no R é consistente com o de NULL no PostgreSQL.

José de Jesus Filho https://rpg.consudata.com.br
01-03-2021

Introdução

Quando você envia um dataframe do R para PostgreSQL, NAs, ou seja, valores faltantes, são convertidos para NULL. Acontece que o R diferencia NA de NULL. No R NULL é tipo de objeto e é por isso que você não consegue incluí-lo num vetor ou numa matriz, mas consegue adicioná-lo como elemento numa lista, inclusive em dataframes, já que estas também são listas. Porém, somente quando todas as colunas são NULL. NULL no R é um tanto ambíguo, pois ora se comporta como vazio, ora como elemento indefinido.

Por sua vez, NA no R é um elemento lógico para indicar dado desconhecido. Isso é concebido dentro da lógica ternária, que admite três valores: VERDADEIRO, FALSO e DESCONHECIDO. O mesmo acontece com SQL para NULL.

Neste tuturial iremos mostrar como o comportamento de NAs no R é consistente com o de NULL no PostgreSQL.

Criando objetos com valores desconhecidos

Para criar objetos com valores desconhecidos no R, você simplesmente coloca NA no vetor ou na coluna:

a <- c(1,2,3,NA)

a
[1]  1  2  3 NA
df <- data.frame(a = a)
df
   a
1  1
2  2
3  3
4 NA

No PostgreSQL, você pode fazer o mesmo, mas com NULL


SELECT  UNNEST(ARRAY[1,2,3,NULL]) AS a;

a

1
2
3

(4 rows)


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

SELECT * FROM df;

a

1
2
3

(4 rows)

Similaridades

Como ambos são valores desconhecidos, espera-se que o comportamento seja similar num e noutro, e de fato é, em muitas situações.

Comparações no R

2 == NA
[1] NA
NA == NA
[1] NA
2 < NA
[1] NA
2 + NA
[1] NA

Em todas essas situações, o R retorna o elemento lógico NA, ou seja, desconhecido, porque o resultado da comparação é de fato desconhecido, já que NA pode equivaler a uma infinidade de valores, inclusive o valor comparado a NA.

Como NULL possui a mesma natureza, as respostas são consistentes:

SELECT 2 = NULL;

?column?

(1 row)


SELECT NULL = NULL;

?column?

(1 row)

SELECT 2 < NULL;

?column?

(1 row)

SELECT 2 + NULL;

?column?

(1 row)

Igualmente, algo que surpreende no R é o filtro negativo em colunas que contêm NAs:

subset(df,a !=2)
  a
1 1
3 3

Com dplyr ocorre o mesmo:

dplyr::filter(df,a != 2)
  a
1 1
2 3

Em ambos os casos, linhas com NAs também foram excluídas. Isso porque NA pode conter o 2 (Confesso que esse comportamento não me convence inteiramente).

Vejamos em SQL

SELECT * FROM df
WHERE  a != 2;

a

1
3
(2 rows)

Para evitar surpresas, não se esqueça de explicitamente filtrar para NAs no R e para NULLs no PostgreSQL.

dplyr::filter(df, a != 2 | is.na(a))
   a
1  1
2  3
3 NA
SELECT * 
FROM df
WHERE a != 2
OR a IS NULL;

a

1
3

(3 rows)

Conclusão

Não estou absolutamente seguro de que os comportamentos de NA no R e NULL em SQL são sempre os mesmos, mas para as operações mais comuns, você pode ficar tranquila que são.