Curso SQL Server — Edição Definitiva

Módulo 9 — NULL vs NOT NULL (lógica de 3 valores, IS NULL/IS NOT NULL, ISNULL, COALESCE, agregações e comparações)

Entenda profundamente o que é NULL, como ele afeta filtros, comparações, agregações, ordenações e concatenação. Aprenda a tratar NULL corretamente no dia a dia.

Objetivo

Dominar a lógica tri-valorada (TRUE, FALSE, UNKNOWN) e aplicar corretamente IS NULL, IS NOT NULL, ISNULL e COALESCE, além de entender como NULL interage com COUNT/SUM/AVG, ORDER BY e CONCAT.

Importante: col = NULL nunca é TRUE — o resultado é desconhecido. Sempre use IS NULL / IS NOT NULL.

NULL (conceito)

O que é / Para que serve: Representa ausência ou desconhecimento de valor.

Sintaxe:

Como funciona: NULL não é zero, nem string vazia, nem '0'. É um marcador especial. Qualquer comparação direta com NULL resulta em UNKNOWN.

Implicações & boas práticas: • Evite permitir NULL sem necessidade. • Prefira NOT NULL com DEFAULT quando fizer sentido.

-- Demonstração da lógica:
SELECT CASE WHEN NULL = NULL THEN 'igual' ELSE 'não é igual' END AS Comparacao; -- 'não é igual'
SELECT CASE WHEN NULL IS NULL THEN 'é nulo' ELSE 'não é nulo' END; -- 'é nulo'

NULL vs NOT NULL (na definição de coluna)

O que é / Para que serve: Controlar se a coluna aceita ausência de valor.

Sintaxe: Coluna TIPO NULL | NOT NULL

Como funciona: Se a coluna for NOT NULL, cada INSERT precisa fornecer valor (ou vir de DEFAULT).

Implicações & boas práticas: • Reduza NULLs desnecessários com DEFAULT. • Em dados obrigatórios, use NOT NULL.

CREATE TABLE dbo.Contatos (
  ContatoID INT IDENTITY PRIMARY KEY,
  Email NVARCHAR(200) NOT NULL,
  Telefone NVARCHAR(20) NULL
);

IS NULL / IS NOT NULL

O que é / Para que serve: Testar nulidade explicitamente em filtros.

Sintaxe: col IS NULL | col IS NOT NULL

Como funciona: Retorna TRUE quando a coluna é (não) nula.

Implicações & boas práticas: • Jamais use = NULL ou <> NULL. • Use IS NULL/IS NOT NULL em WHERE e CASE.

SELECT * FROM dbo.Contatos WHERE Telefone IS NULL; -- correto
-- SELECT * FROM dbo.Contatos WHERE Telefone = NULL; -- incorreto

NULL e agregações (SUM, AVG, COUNT)

O que é / Para que serve: Entender como as funções tratam NULL.

Sintaxe: SUM(col) | AVG(col) | COUNT(*) | COUNT(col)

Como funciona: SUM/AVG ignoram valores NULL; COUNT(col) ignora NULL; COUNT(*) conta linhas (incluindo as com NULL).

Implicações & boas práticas: • Para tratar NULL como 0 em somas/médias, use COALESCE(col,0).

SELECT
  COUNT(*)              AS Linhas,
  COUNT(Telefone)       AS TelefonesInformados, -- ignora NULL
  SUM(COALESCE(Limite,0)) AS LimiteSomadoComZero
FROM dbo.Clientes;

ISNULL(expr, substituto)

O que é / Para que serve: Substituir NULL por um valor padrão (dois argumentos).

Sintaxe: ISNULL(valor, substituto)

Como funciona: Se valor for NULL, retorna substituto, caso contrário retorna valor. O tipo de retorno segue o primeiro argumento.

Implicações & boas práticas: • Simples, mas tem 2 argumentos. • Tipo é do 1º argumento (atenção à precedência e truncamentos).

SELECT ISNULL(NULL, 0) AS Valor;        -- 0
SELECT ISNULL(NULL, 'sem') AS Texto;      -- 'sem'
-- Tipagem: ISNULL(CONVERT(VARCHAR(10),NULL),'abc') retorna VARCHAR(10)

COALESCE(a, b, c, ...)

O que é / Para que serve: Retornar o primeiro argumento não NULL (padrão ANSI).

Sintaxe: COALESCE(a, b [, c ...])

Como funciona: Avalia da esquerda para a direita e retorna o primeiro valor não NULL; o tipo segue regras de precedência entre argumentos.

Implicações & boas práticas: • Aceita vários argumentos (mais flexível que ISNULL). • Padrão ANSI — portável entre SGBDs.

SELECT COALESCE(NULL, NULL, 'primeiro válido', 'outro') AS Resultado;
-- Diferença de tipo: COALESCE(1, 1.5) -> 1.5 (por precedência numérica)

Funções em filtros vs filtros por faixa

O que é / Para que serve: Evitar aplicar função na coluna (quebra de índices) quando possível.

Sintaxe: WHERE YEAR(ColData)=2025 -- EVITE | WHERE ColData >= '2025-01-01' AND ColData < '2026-01-01' -- PREFIRA

Como funciona: Aplicar função na coluna pode impedir uso de índice. Use filtros por faixa (>= e <) para preservar index seek.

Implicações & boas práticas: • Técnica da faixa aberta também evita perda de registros por fração de segundo.

-- Ruim para índice (função na coluna)
SELECT * FROM Vendas WHERE YEAR(CriadoEm)=2025;
-- Bom para índice (faixa)
SELECT * FROM Vendas WHERE CriadoEm >= '2025-01-01' AND CriadoEm < '2026-01-01';

ORDER BY e NULL

O que é / Para que serve: Entender a posição de NULL em ordenações.

Sintaxe: ORDER BY Col [ASC|DESC]

Como funciona: Por padrão, em SQL Server, NULLs aparecem primeiro em ASC e por último em DESC.

Implicações & boas práticas: • Para empurrar NULLs para o fim em ASC, use ORDER BY CASE WHEN Col IS NULL THEN 1 ELSE 0 END, Col.

-- NULLs primeiro (ASC):
SELECT Col FROM (VALUES (NULL),(2),(1)) t(Col) ORDER BY Col ASC;
-- Forçar NULLs por último (ASC):
SELECT Col FROM (VALUES (NULL),(2),(1)) t(Col)
ORDER BY CASE WHEN Col IS NULL THEN 1 ELSE 0 END, Col;

CONCAT e NULL

O que é / Para que serve: Concatenar strings tratando NULL como vazio.

Sintaxe: CONCAT(a, b, ...)

Como funciona: Em SQL Server, CONCAT converte NULL em string vazia. Já o operador + com strings pode retornar NULL se qualquer lado for NULL.

Implicações & boas práticas: • Prefira CONCAT para evitar NULL propagando na concatenação.

SELECT CONCAT('Nome: ', NULL, ' - OK') AS Resultado; -- 'Nome:  - OK'
SELECT 'A' + NULL + 'B'; -- retorna NULL

NOT IN com NULL (armadilha)

O que é / Para que serve: Subconsulta com NULL pode tornar o predicado UNKNOWN e retornar zero linhas.

Sintaxe: col NOT IN (subconsulta)

Como funciona: Se a lista tiver NULL, a comparação vira UNKNOWN para todas as linhas. Prefira NOT EXISTS.

Implicações & boas práticas: • Use NOT EXISTS para evitar armadilhas de NULL em subconsultas.

-- Armadilha:
SELECT * FROM Produtos WHERE Categoria NOT IN (SELECT Cat FROM CategoriasPossiveis); -- se houver NULL em Cat, pode retornar nada
-- Correto:
SELECT p.* FROM Produtos p WHERE NOT EXISTS (
  SELECT 1 FROM CategoriasPossiveis c WHERE c.Cat = p.Categoria
);

Exercícios práticos

  1. Crie dbo.Clientes com Telefone NVARCHAR(20) NULL. Insira linhas com e sem telefone.
  2. Liste apenas clientes sem telefone (IS NULL) e depois os que têm telefone (IS NOT NULL).
  3. Monte um relatório com COUNT(*), COUNT(Telefone) e AVG(COALESCE(Limite,0)).
  4. Crie um exemplo em que NOT IN falha por causa de NULL e reescreva com NOT EXISTS.
  5. Teste CONCAT vs + com uma coluna possivelmente nula.

Referências oficiais & leituras recomendadas