DEV Community

Yuri Peixinho
Yuri Peixinho

Posted on

Funções Avançadas em SQL

Introdução

As funções avançadas em SQL vão além de operações básicas, como selecionar e filtrar dados. Elas permitem realizar cálculos complexos, manipular strings, trabalhar com datas e analisar dados de maneiras mais sofisticadas. Essas funções ajudam a obter insights, transformar dados e criar relatórios mais significativos a partir do seu banco de dados.

Essas funções são dividas em algumas categorias:

  • Funções Numéricas
  • Funções de String
  • Funções Condicionais
  • Funções de Data

Funções Numéricas

FLOOR(x) — arredonda para baixo

Sempre "empurra" o número para o inteiro menor ou igual, independente do sinal.

FLOOR(4.7)-- 4
FLOOR(4.1)-- 4
FLOOR(-4.1)-- -5  (atenção: vai para o lado mais negativo, não trunca!)
Enter fullscreen mode Exit fullscreen mode

Uso real: calcular quantas "páginas cheias" cabem em uma lista, ou converter minutos em horas completas: FLOOR(minutos / 60).

CEILING(x) — arredonda para cima

O oposto do FLOOR: sempre vai para o inteiro maior ou igual.

CEILING(4.1)-- 5
CEILING(-4.7)-- -4
Enter fullscreen mode Exit fullscreen mode

Uso real: calcular quantos caminhões/caixas você precisa. Se cada caixa cabe 10 itens e você tem 23 itens, precisa de CEILING(23/10.0) = 3 caixas (não 2, que sobraria item de fora).

ROUND(x, n) — arredondamento matemático padrão

n é o número de casas decimais, pode ser negativo para arredondar dezenas/centenas.

ROUND(4.567,2)-- 4.57
ROUND(4.565,2)-- 4.56 ou 4.57 (depende do motor — arredondamento bancário vs. "meio para cima")
ROUND(1234,-2)-- 1200  (arredonda para centena mais próxima)
Enter fullscreen mode Exit fullscreen mode

⚠️ Cuidado: o comportamento no .5 exato (round half up vs. round half even/"banker's rounding") varia entre bancos — SQL Server e PostgreSQL não tratam igual. Nunca confie em ROUND para regras fiscais sem testar no seu banco específico.

ABS(x) — valor absoluto

Remove o sinal.

ABS(-15)-- 15
ABS(15)-- 15
Enter fullscreen mode Exit fullscreen mode

Uso real: calcular diferença entre duas datas/valores sem se importar com qual é maior: ABS(preco_atual - preco_anterior) para medir variação, independente de subida ou queda.

MOD(a, b) — resto da divisão

Equivalente ao operador % em outras linguagens. Note que MOD é uma função em Oracle/MySQL; SQL Server usa o operador % diretamente.

MOD(10,3)-- 1
MOD(9,3)-- 0
MOD(-7,3)-- -1 (sinal segue o dividendo `a`, na maioria dos bancos)
Enter fullscreen mode Exit fullscreen mode

Usos reais:

  • Paridade: MOD(id, 2) = 0 → linhas pares (útil para zebra striping em relatórios).
  • Distribuir dados em N grupos/partições: MOD(id, 4) gera 4 grupos (0,1,2,3).
  • Ciclos: dia da semana, rodízio de placas, etc.

Funções de String

LENGTH(s) — tamanho da string

Conta caracteres (não bytes, na maioria dos bancos com suporte a Unicode).

LENGTH('abc')-- 3
LENGTH('')-- 0
LENGTH(NULL)-- NULL (não é 0!)
LENGTH('café')-- 4 caracteres (mas pode variar em bytes se for UTF-8 e você usar OCTET_LENGTH)
Enter fullscreen mode Exit fullscreen mode

⚠️ SQL Server usa LEN() em vez de LENGTH(). Cuidado com espaços em branco à direita — LEN() no SQL Server os ignora, mas LENGTH() no Postgres/MySQL não.

CONCAT(a, b, ...)

Antes do CONCAT existir como função padrão, cada banco usava um operador diferente para juntar strings:

Banco Operador/Sintaxe
PostgreSQL, Oracle `
SQL Server (antigo) {% raw %}+
MySQL CONCAT() (não suporta `

O {% raw %}CONCAT() como função é a forma padrão ANSI, suportada por praticamente todos os bancos modernos — por isso é a escolha mais portável.

A diferença crucial: tratamento de NULL

Essa é a parte mais importante e mais fonte de bugs:

-- Com operador (Postgres, Oracle, SQL Server antigo)
SELECT 'Rua ' || nome_rua|| ', ' || numero;
-- Se numero for NULL → resultado inteiro é NULL!

-- Com CONCAT (padrão ANSI, MySQL, Postgres 9+, SQL Server 2012+)
SELECT CONCAT('Rua ', nome_rua,', ', numero);
-- Se numero for NULL → NULL é tratado como '' (string vazia)
-- Resultado: 'Rua das Flores, '  (não quebra a linha inteira)
Enter fullscreen mode Exit fullscreen mode

Por que isso importa na prática: imagine montar um endereço completo com 5 campos concatenados. Se você usar || e um único campo (tipo complemento, que é opcional) for NULL, o endereço inteiro vira NULL — some da tela. Com CONCAT(), só aquele pedaço fica vazio, o resto aparece normalmente.

-- Exemplo real: monte um endereço, onde "complemento" costuma ser NULL
SELECT CONCAT(logradouro, ', ', numero, ' - ', COALESCE(complemento, ''), ' ', bairro) AS endereco_completo
FROM clientes;
Enter fullscreen mode Exit fullscreen mode

SUBSTRING(s, início, tamanho) — extrai parte da string

Índice começa em 1 (não em 0!).

SUBSTRING('12345678900',1,3)-- '123'
SUBSTRING('12345678900',4,3)-- '456'
SUBSTRING('abcdef',3)-- 'cdef' (sem tamanho, pega até o fim — funciona no Postgres, não em todos)
Enter fullscreen mode Exit fullscreen mode

Usos reais:

  • Extrair parte de um documento: DDD do telefone, os 3 primeiros dígitos do CPF.
  • Mascarar dados sensíveis: mostrar só os últimos 4 dígitos de um cartão.
CONCAT('****',SUBSTRING(cartao,LENGTH(cartao)- 3,4))
Enter fullscreen mode Exit fullscreen mode

REPLACE(s, de, para) — substitui todas as ocorrências

Substitui todas as ocorrências de de por para (não é case-sensitive de forma consistente — depende do collation do banco).

REPLACE('2026-07-21','-','/')-- '2026/07/21'
REPLACE('aaa','a','bb')-- 'bbbbbb'
Enter fullscreen mode Exit fullscreen mode

Uso real: limpar formatação antes de salvar (remover pontos/traços de CPF/CNPJ), normalizar separadores decimais (, → .) vindos de importação de planilha.

UPPER(s) / LOWER(s) — maiúsculas/minúsculas

UPPER('sql')-- 'SQL'
LOWER('SQL')-- 'sql'
Enter fullscreen mode Exit fullscreen mode

Uso real mais importante: comparações case-insensitive sem depender do collation da coluna:

WHERE LOWER(email)= LOWER('Usuario@Email.com')
Enter fullscreen mode Exit fullscreen mode

Isso é comum quando o banco tem collation case-sensitive e você quer garantir que "Joao@x.com" e "JOAO@X.COM" sejam tratados como iguais.

Funções Condicionais

CASE — estrutura condicional

Duas sintaxes:

CASE simples (compara uma expressão contra valores):

CASE status
  WHEN 'A' THEN 'Ativo'
  WHEN 'I' THEN 'Inativo'
  ELSE 'Desconhecido'
END
Enter fullscreen mode Exit fullscreen mode

CASE com busca (searched CASE) — mais flexível, permite condições complexas:

CASE
  WHEN idade < 18 THEN 'Menor'
  WHEN idade BETWEEN 18 AND 65 THEN 'Adulto'
  WHEN idade > 65 THEN 'Idoso'
  ELSE 'Não informado'
END
Enter fullscreen mode Exit fullscreen mode

Regras importantes:

  • Avalia condições em ordem e para na primeira verdadeira — a ordem importa.
  • Se ELSE for omitido e nada bater, retorna NULL.
  • Pode ser usado em SELECTWHEREORDER BY e até dentro de SUM()/COUNT() para agregações condicionais:

NULLIF(a, b) — retorna NULL se forem iguais

NULLIF(5, 5)   -- NULL
NULLIF(5, 3)   -- 5 (retorna 'a' quando são diferentes)
Enter fullscreen mode Exit fullscreen mode

É literalmente um atalho para CASE WHEN a = b THEN NULL ELSE a END

Ou seja: compara a com b. Se forem iguais, retorna NULL. Se forem diferentes, retorna a (nunca b — isso é importante, a função não é simétrica no retorno, mesmo que a comparação seja).

NULLIF(10,10)-- NULL
NULLIF(10,5)-- 10  (retorna 'a', não importa o valor de 'b')
NULLIF(5,10)-- 5
Enter fullscreen mode Exit fullscreen mode

COALESCE(a, b, c, ...) — primeiro valor não-nulo

Percorre a lista da esquerda para a direita e retorna o primeiro argumento que não é NULL.

COALESCE(NULL,NULL,'terceiro','quarto')-- 'terceiro'
COALESCE(telefone_celular, telefone_fixo, email,'sem contato')
Enter fullscreen mode Exit fullscreen mode

Funções de Data e Hora

DATETIMETIMESTAMP — tipos e extração

Não são bem "funções" isoladas — são tipos de dado (DATE = só data, TIME = só hora, TIMESTAMP/DATETIME = ambos), mas também aparecem como funções de conversão/cast:

CAST(coluna_timestampAS DATE)-- extrai só a data, descarta a hora
CAST(coluna_timestampAS TIME)-- extrai só a hora
CURRENT_TIMESTAMP-- data+hora atual (padrão ANSI)
CURRENT_DATE-- só a data atual
Enter fullscreen mode Exit fullscreen mode

Uso real: você tem uma coluna created_at TIMESTAMP e quer agrupar por dia, ignorando a hora:

SELECT CAST(created_at AS DATE) AS dia,COUNT(*)
FROM pedidos
GROUP BY CAST(created_at AS DATE);
Enter fullscreen mode Exit fullscreen mode

Sem esse cast, cada created_at com hora diferente formaria um grupo distinto.

DATEPART(parte, data) — extrai um componente

Sintaxe do SQL Server. Extrai um pedaço específico da data como número.

DATEPART(YEAR,'2026-07-21')-- 2026
DATEPART(MONTH,'2026-07-21')-- 7
DATEPART(DAY,'2026-07-21')-- 21
DATEPART(WEEKDAY,'2026-07-21')-- dia da semana (numérico)
DATEPART(QUARTER,'2026-07-21')-- 3 (terceiro trimestre)
Enter fullscreen mode Exit fullscreen mode

Equivalentes em outros bancos:

  • PostgreSQL: EXTRACT(YEAR FROM data)
  • MySQL: YEAR(data)MONTH(data)DAY(data)

Uso real: relatórios agrupados por período — vendas por mês, por trimestre, por ano:

SELECT DATEPART(YEAR, data_pedido) AS ano,DATEPART(MONTH, data_pedido) AS mes,SUM(valor)
FROM pedidos
GROUP BY DATEPART(YEAR, data_pedido), DATEPART(MONTH, data_pedido);
Enter fullscreen mode Exit fullscreen mode

DATEADD(parte, n, data) — soma/subtrai intervalo

DATEADD(DAY,30,'2026-07-21')-- 2026-08-20
DATEADD(MONTH,-1,'2026-07-21')-- 2026-06-21
DATEADD(YEAR,1,'2026-07-21')-- 2027-07-21
Enter fullscreen mode Exit fullscreen mode

n negativo subtrai. Equivalentes:

  • PostgreSQL: data + INTERVAL '30 days'
  • MySQL: DATE_ADD(data, INTERVAL 30 DAY)

Usos reais:

  • Data de vencimento: DATEADD(DAY, 30, data_pedido).
  • Janela de tempo relativa a hoje: WHERE data_pedido >= DATEADD(DAY, -7, GETDATE()) → últimos 7 dias.
  • Calcular idade: DATEDIFF(YEAR, data_nascimento, GETDATE()) (função irmã do DATEADD).

Top comments (0)