Módulo 4: Texto
||, ILIKE, LOWER/UPPER/INITCAP, LENGTH, TRIM, LEFT/RIGHT, SPLIT_PART, POSITION, REPLACE
Antes de começar
Texto sujo é meio ambiente natural do QA: espaço sobrando, caixa trocada, e-mail com domínio errado. O Postgres tem um bom kit pra investigar isso.
a || b: junta textos:nome || ' (' || estado || ')'. Se um lado for NULL o resultado é NULL (useCOALESCE).ILIKE:LIKEsem ligar pra maiúsc./minúsc.~/~*fazem regex (sensível / insensível).- Caixa:
LOWER,UPPER,INITCAP("joão silva" → "João Silva"). - Recortar:
LEFT(t, 3),RIGHT(t, 4),SUBSTRING(t FROM 2 FOR 5),SPLIT_PART(email, '@', 2)(pega o pedaço 2, o domínio). - Achar/trocar:
POSITION('@' IN email)(0 = não achou),REPLACE(t, 'a', 'b'),TRIM(t),LENGTH(t).
Olhar de QA: "quantos e-mails não são do domínio esperado?", "tem nome com espaço no fim?", "busca deveria ser case-insensitive e não é".
Dois exemplos
Teoria
a || b || c: concatena.CONCAT_WS(', ', a, b)ignora NULL e põe separador.t ILIKE '%abc%': contém "abc" em qualquer caixa;t ~* 'abc'é regex insensível.LOWER / UPPER / INITCAP(t): caixa.SPLIT_PART(email, '@', 2): domínio.LEFT/RIGHT(t, n): pontas.POSITION('x' IN t): índice de "x" (0 = ausente).LENGTH(t): tamanho.TRIM(t)tira espaços das pontas;t = TRIM(t)é falso quando sobra espaço.
Exemplos de aplicação
Um exemplo montado pra cada item da teoria acima. Não precisa rodar, é pra ver a cara do recurso e entender cada pedaço. Os valores são ilustrativos.
|| e CONCAT_WS
SELECT
nome || ' - ' || estado AS rotulo,
CONCAT_WS(', ', cidade, estado) AS local
FROM clientes;
|| gruda textos; se qualquer pedaço for NULL, o resultado inteiro vira NULL. CONCAT_WS(sep, ...) junta com um separador e ignora os NULL, ficando mais seguro pra montar endereço.
ILIKE e regex ~*
SELECT titulo
FROM defeitos
WHERE titulo ILIKE '%login%'
OR titulo ~* 'senha|password';
ILIKE '%login%' = contém "login" em qualquer caixa. ~* 'senha|password' é regex insensível a caixa: casa "senha" ou "password". Regex serve quando o padrão é mais complexo que um simples "contém".
LOWER / UPPER / INITCAP
SELECT
nome,
LOWER(email) AS email_normalizado,
INITCAP(nome) AS nome_padronizado
FROM clientes;
LOWER/UPPER forçam a caixa toda; INITCAP deixa a primeira letra de cada palavra maiúscula. Comparar sempre com a mesma caixa (LOWER(email) = LOWER(entrada)) evita bug de "achou" vs "não achou" por causa de maiúscula.
SPLIT_PART, LEFT / RIGHT
SELECT
email,
SPLIT_PART(email, '@', 2) AS dominio,
LEFT(email, 3) AS inicio
FROM clientes;
SPLIT_PART(texto, '@', 2) quebra o texto no @ e pega o 2º pedaço: o domínio do email. LEFT(t, n)/RIGHT(t, n) pegam os n primeiros/últimos caracteres.
POSITION, LENGTH
SELECT
email,
POSITION('@' IN email) AS pos_arroba,
LENGTH(email) AS tamanho
FROM clientes
WHERE POSITION('@' IN email) = 0; -- email sem @
POSITION('x' IN t) devolve em que caractere "x" aparece, ou 0 se não aparece, útil pra validar formato. LENGTH(t) é o número de caracteres.
TRIM e espaço sobrando
SELECT id, cidade
FROM clientes
WHERE cidade <> TRIM(cidade); -- tem espaço nas pontas
TRIM(t) remove espaços do começo e do fim. Se cidade for diferente de TRIM(cidade), é porque sobrou espaço, um problema clássico de dado "sujo" que quebra comparação e agrupamento.
Pegadinhas deste módulo
Os erros que quase todo mundo comete aqui. Ler agora poupa meia hora de confusão depois, e alguns deles são a causa de relatório errado em projeto real.
- A armadilha clássica: filtrar a tabela da direita no
WHEREtransforma oLEFT JOINnumINNER JOIN, em silêncio.
Por quê: o-- ERRADO: vira INNER JOIN e perde os clientes sem pedido SELECT c.nome, p.id FROM clientes c LEFT JOIN pedidos p ON p.cliente_id = c.id WHERE p.status = 'entregue'; -- CERTO: a condição vai no ON SELECT c.nome, p.id FROM clientes c LEFT JOIN pedidos p ON p.cliente_id = c.id AND p.status = 'entregue';LEFT JOINpreenche comNULLquem não tem par, e oWHERElogo em seguida descarta essas linhas, porqueNULL = 'entregue'não é verdadeiro. O resultado parece certo e está faltando gente. Regra: condição sobre a tabela da direita vai noON; a única exceção éWHERE x IS NULL, que é justamente como se procura "quem não tem". JOINsemONmultiplica tudo. Esquecer a condição gera o produto cartesiano: 200 pedidos × 60 clientes = 12 000 linhas. Se o resultado veio absurdamente grande, é a primeira coisa a conferir.JOINcom relação um-para-muitos duplica as linhas da esquerda. Juntarpedidoscomitens_pedidorepete o pedido uma vez por item. AíSUM(p.valor_total)soma o mesmo pedido várias vezes e infla o total. É o erro de número mais difícil de perceber, porque o resultado parece plausível.- Sempre qualifique a coluna quando o nome existe nas duas tabelas.
idsozinho numJOINdepedidoscomclientesé ambíguo: o banco reclama, ou pior, escolhe um. - Confira a contagem antes e depois do
JOIN. Sepedidostem 200 linhas e o seuJOINdevolveu 512, houve duplicação. Esse hábito de dois segundos evita relatório errado.
Prática
Escreva a query e clique Rodar (ou Ctrl+Enter).
carregando banco…