SQL Funções de Texto

Os dados da Loja Aurora estão cheios de texto: nomes de clientes, emails, cidades, telefones, nomes de produtos. E quase nunca do jeito que a Ana precisa mostrar. Ela quer o primeiro nome do cliente para o email de aniversário, o domínio de cada email, o telefone só com números, a cidade e o estado juntos ("Teresina - PI") e o preço com duas casas ("4,90"). Tudo isso se faz no próprio SELECT, com as funções de texto. Nesta aula você vai conhecer as principais e as armadilhas de cada uma, incluindo a mais traiçoeira para quem escreve em português: o upper do SQLite não entende acentos.

Você vai aprender
  • Medir um texto com length.
  • Trocar maiúsculas e minúsculas com upper e lower, e o que elas fazem com é, ç e ã.
  • Recortar pedaços com substr e achar posições com instr.
  • Limpar com trim e trocar trechos com replace.
  • Juntar textos com ||, e por que um NULL estraga a junção inteira; concat e concat_ws.
  • Formatar números e textos com printf (ou format): 4.9 vira 4.90.

Os exemplos rodam de verdade no seu navegador, num SQLite com o banco da Loja Aurora, e cada exemplo parte do banco limpo. Texto, em SQL, vai sempre entre aspas simples: 'Ana'.


A ideia: a tesoura, a cola e o carimbo

Pense no balcão da loja com uma ficha de papel de cada cliente. Para preparar as etiquetas de envio, a Ana usa três ferramentas:

  • a tesoura, para recortar só um pedaço: o primeiro nome, o DDD do telefone (substr, trim);
  • a cola, para juntar pedaços: cidade + " - " + estado (||, concat);
  • os carimbos, que mudam a cara do texto sem mudar o conteúdo: tudo em maiúsculas, o preço com duas casas (upper, printf).

As funções de texto são essas ferramentas. Um detalhe importante: elas não mudam a tabela. Um SELECT upper(nome) FROM clientes mostra os nomes em maiúsculas, mas os nomes guardados continuam como estavam. Só um UPDATE (aula UPDATE) muda o que está guardado.

Toda função tem o mesmo molde: o nome, e entre parênteses o que ela recebe (os argumentos), separados por vírgula: substr(nome, 1, 3).


1. length: quantas letras

SELECT nome, length(nome) AS letras
FROM clientes
ORDER BY letras DESC
LIMIT 3;

length ("comprimento") conta os caracteres do texto, contando os espaços. O nome mais comprido da loja é "Gustavo Henrique Prado", com 22. As letras acentuadas contam como uma letra cada, como deveria ser:

SELECT nome,
       length(nome)       AS letras,
       octet_length(nome) AS bytes
FROM clientes
WHERE id IN (3, 5);

"Conceição Prado" tem 15 letras, mas ocupa 17 bytes no arquivo: o ç e o ã gastam 2 bytes cada (é assim que o formato UTF-8 guarda letras acentuadas). A octet_length ("comprimento em octetos", ou bytes) mostra isso. Para contar letras, use sempre a length.

EscritaResultadoPor quê
length('Ana')3Três letras
length('')0Texto vazio
length(NULL)NULLSem valor, sem tamanho (os clientes sem email têm length(email) NULL)
length(12.5)4Um número é medido como texto: '12.5'

2. upper e lower: maiúsculas e minúsculas

upper ("de cima", as letras maiúsculas) e lower ("de baixo", as minúsculas) trocam a caixa das letras. Com nomes sem acento, tudo certo:

SELECT upper('Ana Beatriz Sousa') AS maiusculas,
       lower('Ana Beatriz Sousa') AS minusculas;

Agora com os nomes acentuados da loja:

SELECT nome, upper(nome) AS maiusculas, lower(nome) AS minusculas
FROM clientes
WHERE id IN (1, 5, 9);

Olhe com atenção: JOSé RIBAMAR SILVA, CONCEIçãO PRADO, ANTôNIO CARLOS NEVES. As letras acentuadas não mudaram. O upper e o lower do SQLite só sabem trocar as 26 letras do alfabeto inglês, de a a z (o conjunto ASCII). O é, o ç, o ã e o ô passam direto, do jeito que estavam. É uma escolha do SQLite, para ficar pequeno: tratar maiúsculas e minúsculas de todas as línguas do mundo exige tabelas grandes.

O que isso quebra
  • Etiquetas em maiúsculas com acento saem misturadas (CONCEIçãO).
  • Buscas que ignoram maiúsculas. WHERE upper(nome) LIKE '%CONCEIÇÃO%' não acha a Conceição, porque upper('Conceição') é 'CONCEIçãO', e não 'CONCEIÇÃO'. Pelo mesmo motivo, o LIKE (aula LIKE) ignora maiúsculas só nas letras sem acento: 'Ana' LIKE 'ana' é verdadeiro, mas 'Água' LIKE 'água' é falso.

O que fazer: para emails e códigos (que não têm acento), lower funciona perfeitamente. Para nomes, guarde os dados já bem escritos e busque como eles estão guardados, ou deixe as maiúsculas para o programa que mostra os dados (PHP, Python, JavaScript sabem lidar com acentos). Existe também uma extensão do SQLite, a ICU, que ensina acentos ao upper e ao lower; ela não vem no SQLite padrão nem no editor do guia.

SELECT 'Ana' LIKE 'ana'                AS sem_acento,
       'Água' LIKE 'água'              AS com_acento,
       upper('josé') = 'JOSÉ'          AS upper_com_acento,
       lower('ANA.BEATRIZ@EXEMPLO.COM.BR') AS email_minusculo;

Os resultados 1 e 0 são o "verdadeiro" e o "falso" do SQLite. O email ficou certinho em minúsculas: só tinha letras sem acento.


3. substr: recortar um pedaço

substr (de substring, "pedaço do texto") recorta um trecho. Ela recebe o texto, a posição onde começa e, se quiser, quantos caracteres levar. A primeira letra é a posição 1:

SELECT substr('Ana Beatriz Sousa', 1, 3)  AS do_1_leva_3,
       substr('Ana Beatriz Sousa', 5, 7)  AS do_5_leva_7,
       substr('Ana Beatriz Sousa', 5)     AS do_5_ate_o_fim,
       substr('Ana Beatriz Sousa', -5)    AS ultimas_5;
Posição1234567891011121314151617
LetraAna␣Beatriz␣Sousa

(O ␣ marca os espaços: eles também ocupam uma posição.)

substr(texto, 1, 3)
Começa na posição 1 e leva 3: Ana.
substr(texto, 5, 7)
Começa na posição 5 (o B) e leva 7: Beatriz.
substr(texto, 5)
Sem o terceiro argumento, vai até o fim: Beatriz Sousa.
substr(texto, -5)
Posição negativa conta do fim para o começo: as 5 últimas, Sousa.

O SQLite também aceita o nome substring, do SQL padrão, com os mesmos argumentos.

instr: onde está?

Para recortar o primeiro nome, a posição do corte muda de cliente para cliente: "Ana" tem 3 letras, "Carlos" tem 6. A instr (de in string, "no texto") diz em que posição um trecho aparece pela primeira vez, ou 0 se não aparece:

SELECT nome,
       instr(nome, ' ') AS posicao_do_espaco,
       substr(nome, 1, instr(nome, ' ') - 1) AS primeiro_nome
FROM clientes
WHERE id <= 4;

Em "José Ribamar Silva", o primeiro espaço está na posição 5. O primeiro nome vai da posição 1 e leva 5 − 1 = 4 letras: "José". O - 1 é para não levar o espaço junto. O mesmo truque separa as duas partes de um email:

SELECT email,
       substr(email, 1, instr(email, '@') - 1) AS usuario,
       substr(email, instr(email, '@') + 1)    AS dominio
FROM clientes
WHERE id <= 3;

O usuário é tudo antes do @ (até a posição dele − 1); o domínio é tudo depois (da posição dele + 1 até o fim).

Experimente: a Ana vai mandar um email de aniversário que começa com "Olá, José!". Ela precisa só do primeiro nome de cada cliente. Troque o nome do SELECT por um substr com instr que recorte até antes do primeiro espaço, mantendo o apelido primeiro_nome.

SELECT nome AS primeiro_nome
FROM clientes;
SELECT substr(nome, 1, instr(nome, ' ') - 1) AS primeiro_nome
FROM clientes;

4. trim e replace: limpar e trocar

trim: tirar as sobras das pontas

Quem digita um cadastro às vezes deixa espaços sobrando no começo ou no fim: ' Ana '. Para o banco, 'Ana' e 'Ana ' são textos diferentes, e uma busca por 'Ana' não acha o segundo. A trim ("aparar") tira os espaços das duas pontas; ltrim só da esquerda (left) e rtrim só da direita (right). As barras | no exemplo estão lá só para você enxergar onde o texto acaba:

SELECT '|' || trim('   Ana   ')  || '|' AS com_trim,
       '|' || ltrim('   Ana   ') || '|' AS com_ltrim,
       '|' || rtrim('   Ana   ') || '|' AS com_rtrim,
       trim('--Ana--', '-')             AS outro_caractere;

Com um segundo argumento, a trim tira outros caracteres das pontas: trim('--Ana--', '-') tira os hífens. Ela nunca mexe no meio do texto: trim('Ana Lima') mantém o espaço entre os nomes.

replace: trocar um trecho por outro

replace ("substituir") troca todas as vezes que um trecho aparece. Ela recebe o texto, o que procurar e o que pôr no lugar:

SELECT replace('Caneta Esferográfica Azul', 'Azul', 'Preta') AS outra_cor,
       replace('banana', 'a', 'o')                            AS todas_as_vezes,
       replace('(86) 99821-4455', '-', '')                    AS sem_hifen;

Para deixar o telefone só com números, é preciso tirar quatro coisas: parênteses, espaço e hífen. Uma replace dentro da outra, de dentro para fora:

SELECT nome, telefone,
       replace(replace(replace(replace(telefone, '(', ''), ')', ''), ' ', ''), '-', '') AS so_numeros
FROM fornecedores;

Leia de dentro para fora: a replace mais de dentro tira o (; a de fora dela tira o ); depois o espaço; por último o hífen. Os telefones de Portugal e da Argentina ficaram com o + do código do país, porque ele não estava na lista.


5. Juntar textos: ||, concat e concat_ws

No SQL padrão, quem junta (ou concatena) textos é o operador ||, duas barras verticais. Ele cola o da esquerda com o da direita, sem pôr nada no meio; os espaços e separadores são por sua conta:

SELECT nome,
       cidade || ' - ' || estado AS local
FROM clientes
WHERE id <= 3;

Três pedaços colados: a cidade, o texto ' - ' (espaço, hífen, espaço) e o estado. Números também podem entrar na junção, e viram texto: 'Total: ' || 30.94 dá Total: 30.94.

O NULL estraga a junção inteira

Agora com os fornecedores. Dois deles são estrangeiros e estão com estado NULL:

SELECT nome, cidade, estado,
       cidade || ' - ' || estado AS local
FROM fornecedores;

Para a Lusitana Comércio (Porto) e a Pampa Sur (Buenos Aires), o local inteiro virou NULL, e a cidade, que existia, sumiu junto. É a regra do NULL (aula O Valor NULL): qualquer conta com um valor desconhecido dá desconhecido, e juntar textos é uma conta. Três jeitos de consertar:

SELECT nome,
       cidade || ' - ' || coalesce(estado, pais) AS com_coalesce,
       concat(cidade, ' - ', estado)             AS com_concat,
       concat_ws(' - ', cidade, estado)          AS com_concat_ws
FROM fornecedores;
coalesce(estado, pais)
Usa o estado; se ele for NULL, usa o país (aula IFNULL e COALESCE). Resultado: Porto - Portugal.
concat(a, b, c, …)
Junta todos os argumentos, tratando NULL como texto vazio. Não some com a cidade, mas sobra o separador: Porto - , com o hífen pendurado.
concat_ws(separador, a, b, …)
"Concat with separator", com separador. O primeiro argumento é o separador, posto entre os outros, e os NULL são pulados junto com o seu separador: Porto, limpinho.

Missão: a lista de fornecedores com o local perdeu Porto e Buenos Aires, porque o estado deles é NULL. Conserte a coluna local para que nenhuma cidade suma. Pode ser com coalesce, concat ou concat_ws: você escolhe.

SELECT nome,
       cidade || ' - ' || estado AS local
FROM fornecedores;
SELECT nome,
       concat_ws(' - ', cidade, estado) AS local
FROM fornecedores;

6. printf e format: carimbar números e textos

O SQLite escreve os números do jeito mais curto: o marca-texto custa 4.9, e não 4.90. Para uma etiqueta de preço, isso é feio. A printf ("imprima formatado", um nome que vem da linguagem C) recebe um molde e os valores que entram nele. O SQLite também a chama de format; as duas são a mesma função.

SELECT nome, preco,
       printf('%.2f', preco)                       AS duas_casas,
       format('R$ %.2f', preco)                    AS com_moeda,
       replace(printf('%.2f', preco), '.', ',')    AS jeito_brasileiro
FROM produtos
WHERE id IN (1, 4, 9, 14);
'%.2f'
Um número com 2 casas depois do ponto (f de float, número com vírgula). 4.9 vira 4.90; 99.0 vira 99.00.
'R$ %.2f'
O que está fora do código é copiado como está: o R$ fica na frente.
replace(…, '.', ',')
A printf usa ponto. Para o formato brasileiro, troque o ponto pela vírgula.

O resultado da printf é texto, pronto para mostrar. Não use esse texto para fazer contas nem para ordenar: para isso, use o número original (ou a round, da aula Funções Numéricas).

Os códigos da printf

SELECT printf('%05d', 42)                AS cinco_digitos,
       printf('%s tem %d anos', 'Ana', 30) AS frase,
       printf('%d%%', 15)                 AS porcentagem,
       printf('[%-6s]', 'Ana')            AS alinhado_esquerda,
       printf('[%6s]', 'Ana')             AS alinhado_direita;
CódigoO que põe no lugarExemplo
%sUm texto (string)printf('Olá, %s!', 'Ana') → Olá, Ana!
%dUm número inteiro (decimal)printf('%d itens', 3) → 3 itens
%.2fNúmero com 2 casas (troque o 2 por quantas quiser)printf('%.1f', 4.25) → 4.3
%05dInteiro com 5 dígitos, completando com zeros à esquerdaprintf('%05d', 42) → 00042
%6s / %-6sTexto ocupando 6 espaços, alinhado à direita / à esquerda[ Ana] / [Ana ]
%%Um % de verdadeprintf('%d%%', 15) → 15%

Cada código pega o próximo valor da lista, na ordem: em printf('%s tem %d anos', 'Ana', 30), o %s pega 'Ana' e o %d pega 30.


7. Formas diferentes de fazer

Muitas tarefas de texto têm mais de uma solução. Para "cidade - estado, sem perder ninguém":

FormaCom estado NULL dáQuando usar
cidade || ' - ' || estadoNULL (perde tudo)Quando você tem certeza de que nada é NULL (ex.: colunas NOT NULL). É o SQL padrão.
cidade || ' - ' || coalesce(estado, '?')Porto - ?Quando quer um valor no lugar do NULL.
concat(cidade, ' - ', estado)Porto - Para juntar sem perder nada; existe também no MySQL, PostgreSQL e SQL Server.
concat_ws(' - ', cidade, estado)PortoPara listas com separador (endereço, nome completo), pulando o que faltar.
printf('%s - %s', cidade, estado)Porto - Quando a junção também precisa formatar números.

E para "o primeiro nome": substr(nome, 1, instr(nome, ' ') - 1) funciona para todo nome com espaço. Para nomes de uma palavra só, veja o primeiro caso de Quando dá errado.


Quando dá errado

O primeiro nome de quem não tem sobrenome
SELECT instr('Maria', ' ')                        AS posicao,
       substr('Maria', 1, instr('Maria', ' ') - 1) AS primeiro_nome;

O que aconteceu: o primeiro nome saiu vazio. "Maria" não tem espaço, a instr devolveu 0, e 0 - 1 é -1: pedir −1 letras dá nada. Todos os clientes da loja têm sobrenome, mas um cadastro novo pode não ter.

Como consertar: acrescente um espaço no fim antes de procurar, para sempre achar um: substr(nome, 1, instr(nome || ' ', ' ') - 1).

Juntou com +
SELECT 'Ana' + ' Lima' AS com_mais,
       'Ana' || ' Lima' AS com_barras;

O que aconteceu: 0. No SQLite, + é sempre soma de números; ele tentou transformar 'Ana' e ' Lima' em números, não conseguiu, usou 0 para cada um e somou: 0. Sem erro. (No SQL Server, + junta textos; veja "Nos outros bancos".)

Como consertar: no SQLite, junte com || ou concat.

Contou as posições a partir do zero
SELECT substr('Ana Beatriz', 0, 3) AS comecando_do_zero,
       substr('Ana Beatriz', 1, 3) AS comecando_do_um;

O que aconteceu: veio só An. Quem programa em linguagens como Python e JavaScript está acostumado a contar do zero, mas no SQL a primeira letra é a posição 1. A posição 0 fica "antes" do texto e gasta uma das 3 letras pedidas.

Como consertar: comece do 1.

Aspas duplas no texto
SELECT nome
FROM clientes
WHERE cidade = "Teresina";

O que aconteceu: no such column: "Teresina" - should this be a string literal in single-quotes?, "não existe a coluna "Teresina"; deveria ser um texto entre aspas simples?". Em SQL, aspas duplas são para nomes (de colunas e tabelas). Texto é sempre com aspas simples.

Como consertar: WHERE cidade = 'Teresina'.

Um apóstrofo dentro do texto

O fornecedor 2 se chama Loja D'Água. O apóstrofo é a mesma aspa simples que fecha o texto, então 'Loja D'Água' termina no D, e o resto vira lixo de sintaxe:

SELECT nome, cidade
FROM fornecedores
WHERE nome = 'Loja D'Água';

O que aconteceu: near "Água": syntax error, "erro de sintaxe perto de Água": o pedaço que ficou fora das aspas.

Como consertar: dentro de um texto, um apóstrofo se escreve com duas aspas simples seguidas: 'Loja D''Água'.

SELECT nome, cidade
FROM fornecedores
WHERE nome = 'Loja D''Água';
Usou uma função de outro banco
SELECT LEFT(nome, 3) AS inicio
FROM clientes;

O que aconteceu: no such function: LEFT. O LEFT ("esquerda") existe no MySQL e no SQL Server, mas não no SQLite.

Como consertar: substr(nome, 1, 3). Para o RIGHT, substr(nome, -3).

Nos outros bancos

Os nomes e alguns comportamentos mudam bastante de um banco para outro. Trechos ilustrativos, que não rodam no SQLite:

TarefaSQLitePostgreSQLMySQLSQL Server
Juntara || b, concata || b, concatCONCAT(a, b) (o || é "ou" lógico, a não ser num modo especial)a + b, CONCAT(a, b)
Tamanho em letraslengthlength, char_lengthCHAR_LENGTH (o LENGTH conta bytes)LEN (ignora espaços no fim)
Maiúsculas com acentoNão (só A–Z)SimSimSim
Começo do textosubstr(s, 1, 3)left(s, 3), substrLEFT(s, 3)LEFT(s, 3), SUBSTRING
Posiçãoinstr(s, t)strpos(s, t), position(t IN s)INSTR(s, t), LOCATE(t, s)CHARINDEX(t, s)

O caso do MySQL merece atenção: SELECT 'a' || 'b', que no SQLite dá ab, no MySQL (no modo padrão) é uma conta de "ou" lógico e dá 0. Use CONCAT quando o mesmo SQL tiver de rodar nos dois. E o LENGTH('Conceição') do MySQL dá 11 (bytes), e não 9.


Referência rápida

FunçãoO que fazExemplo → resultado
length(s)Número de caractereslength('Conceição') → 9
octet_length(s)Número de bytesoctet_length('Conceição') → 11
upper(s) / lower(s)Maiúsculas / minúsculas (só A–Z!)upper('josé') → JOSé
substr(s, início, qtd)Recorta (a 1ª letra é a posição 1; negativo conta do fim)substr('Aurora', 1, 3) → Aur
instr(s, t)Posição da 1ª vez que t aparece; 0 se não apareceinstr('ana@x', '@') → 4
trim(s), ltrim, rtrimTira espaços (ou os caracteres dados) das pontastrim(' Ana ') → Ana
replace(s, de, para)Troca todas as ocorrênciasreplace('a-b-c', '-', '') → abc
a || bJunta (com NULL, dá NULL)'Ana' || ' Lima' → Ana Lima
concat(a, b, …)Junta, tratando NULL como vazioconcat('Ana', NULL) → Ana
concat_ws(sep, a, b, …)Junta com separador, pulando os NULLconcat_ws(', ', 'a', NULL, 'b') → a, b
printf(molde, …) = formatFormata (%s, %d, %.2f, %05d, %%)printf('%.2f', 4.9) → 4.90

Exercícios

1. Os três primeiros caracteres

Complete para pegar as 3 primeiras letras do nome:

SELECT substr(nome, , 3) FROM produtos;

2. Juntar com separador

Complete o operador que junta textos no SQL padrão:

SELECT cidade ' - ' estado FROM clientes;

3. O que upper('Conceição') devolve no SQLite?

4. O fornecedor Pampa Sur tem estado NULL. O que cidade || ' - ' || estado mostra para ele?

5. Qual expressão mostra o preço 4.9 como 4.90?

6. Quanto vale instr('ana.beatriz@exemplo.com.br', '@')?

Missão final: as etiquetas da papelaria

Missão: a papelaria (categoria 1) vai ganhar etiquetas novas. Para cada produto dela, mostre três colunas: codigo, a letra P seguida do id com 3 dígitos (o produto 4 vira P004); produto, o nome; e preco_br, o preço com duas casas e vírgula (4,90).

SELECT id, nome, preco
FROM produtos
WHERE categoria_id = 1;
SELECT 'P' || printf('%03d', id)                AS codigo,
       nome                                      AS produto,
       replace(printf('%.2f', preco), '.', ',')  AS preco_br
FROM produtos
WHERE categoria_id = 1;

Outra resposta certa para o código: printf('P%03d', id), com o P dentro do molde.


Resumo

  • Funções de texto mudam o que o SELECT mostra, nunca o que está guardado.
  • length conta caracteres (com acento, 1 cada); octet_length conta bytes.
  • upper e lower no SQLite só trocam as letras A–Z: upper('Conceição') dá CONCEIçãO. Use-as sem medo em emails e códigos.
  • substr(s, início, qtd) recorta, contando do 1; instr(s, t) acha a posição (0 se não acha). Juntas, separam primeiro nome, usuário e domínio do email.
  • trim limpa as pontas; replace troca todas as ocorrências.
  • || junta textos, mas um NULL derruba tudo; concat trata NULL como vazio e concat_ws pula o NULL junto com o separador. + não junta textos no SQLite.
  • printf (ou format) carimba: '%.2f' para duas casas, '%05d' para zeros à esquerda. O resultado é texto, para mostrar.