INITCAP e uma funcao do PostgreSQL que coloca em maiuscula a primeira letra de cada palavra e o resto em minuscula. E util para normalizar nomes, cidades e rotulos para um title-case uniforme, mas traz algumas limitacoes pouco obvias que vale conhecer de antemao.
O que o INITCAP faz
INITCAP recebe uma string, divide em palavras e, para cada palavra, eleva a primeira letra a maiuscula enquanto forca todas as demais a minuscula. Uma palavra e qualquer sequencia de caracteres alfanumericos; um limite de palavra e qualquer caractere nao alfanumerico (espaco, ponto, hifen, apostrofo e assim por diante).
SELECT initcap('john DOE');
SELECT initcap('HELLO world');
SELECT initcap('order #42 paid');
Repare que a caixa interna original nao e preservada. INITCAP sempre normaliza cada palavra para "primeira letra maiuscula, o resto minusculo", entao 'McDONALD' vira 'Mcdonald'.
Normalizar nomes e cidades
O caso classico sao dados que chegaram de um formulario ou de uma importacao com caixa arbitraria e que precisam ser exibidos de forma limpa.
SELECT id, initcap(name) AS display_name
FROM users
ORDER BY display_name;
SELECT initcap(country) AS country, count(*) AS users
FROM users
GROUP BY initcap(country)
ORDER BY users DESC;
Voce tambem pode gravar de volta o valor normalizado, se quiser dados limpos em repouso:
UPDATE users
SET name = initcap(name)
WHERE name <> initcap(name);
Onde o INITCAP quebra
A grande armadilha e que o INITCAP trata qualquer caractere nao alfanumerico como limite de palavra. Para nomes reais isso costuma estar errado:
- um apostrofo inicia uma "nova palavra", entao a letra seguinte recebe maiuscula;
- hifens e apostrofos dentro de um nome sao todos tratados como separadores;
- maiusculas internas (como em
McDonald) se perdem: caem para minuscula.
SELECT initcap('o''brien');
SELECT initcap('jean-luc PICARD');
SELECT initcap('mcdonald');
Pegadinha: o INITCAP nao conhece regras culturais como McDonald, van der Berg ou DeShawn. Ele sempre coloca maiuscula depois de um apostrofo ou hifen, e perde as maiusculas internas de uma palavra. Para marcas e sobrenomes com grafia especial, nao confie no INITCAP — guarde a forma canonica separadamente.
A segunda sutileza e o locale e o Unicode. O PostgreSQL muda a caixa conforme o locale do banco, e para alguns pares (o i/I turco, por exemplo) o resultado pode diferir do esperado. Nomes ASCII sao previsiveis, mas com dados multilingues verifique o comportamento no seu proprio locale.
Se o INITCAP so estraga algumas linhas, nao grave o resultado na tabela inteira: selecione as linhas problematicas e corrija o resto no lugar:
UPDATE users
SET name = initcap(name)
WHERE name <> initcap(name)
AND name !~ '[''-]';
INITCAP no MySQL: emulacao
O MySQL nao tem INITCAP. Para uma unica palavra e facil montar com UPPER, LOWER e SUBSTRING:
SELECT CONCAT(
UPPER(SUBSTRING(name, 1, 1)),
LOWER(SUBSTRING(name, 2))
) AS display_name
FROM users;
Para strings de varias palavras essa expressao nao basta: e preciso processar cada palavra. Na pratica os times resolvem com uma funcao armazenada que percorre a string e coloca em maiuscula a letra depois de cada espaco, ou empurram a logica para a camada de aplicacao. O ClickHouse tambem nao tem INITCAP, mas oferece upperUTF8, lowerUTF8 e substring, com os quais se constroi a mesma emulacao.
Resumo: no PostgreSQL o INITCAP e otimo para normalizar rapido strings ASCII simples, mas nao confie nele para sobrenomes com apostrofos, hifens ou maiusculas internas; no MySQL e no ClickHouse voce obtem o mesmo efeito na mao com UPPER + LOWER + SUBSTRING.
INITCAPe uma funcao do PostgreSQL que coloca em maiuscula a primeira letra de cada palavra e o resto em minuscula. E util para normalizar nomes, cidades e rotulos para um title-case uniforme, mas traz algumas limitacoes pouco obvias que vale conhecer de antemao.O que o INITCAP faz
INITCAPrecebe uma string, divide em palavras e, para cada palavra, eleva a primeira letra a maiuscula enquanto forca todas as demais a minuscula. Uma palavra e qualquer sequencia de caracteres alfanumericos; um limite de palavra e qualquer caractere nao alfanumerico (espaco, ponto, hifen, apostrofo e assim por diante).SELECT initcap('john DOE'); -- John Doe SELECT initcap('HELLO world'); -- Hello World SELECT initcap('order #42 paid'); -- Order #42 PaidRepare que a caixa interna original nao e preservada.
INITCAPsempre normaliza cada palavra para "primeira letra maiuscula, o resto minusculo", entao'McDONALD'vira'Mcdonald'.Normalizar nomes e cidades
O caso classico sao dados que chegaram de um formulario ou de uma importacao com caixa arbitraria e que precisam ser exibidos de forma limpa.
-- Tidy up names captured in mixed or all-caps form SELECT id, initcap(name) AS display_name FROM users ORDER BY display_name; -- Normalize country labels for a report SELECT initcap(country) AS country, count(*) AS users FROM users GROUP BY initcap(country) ORDER BY users DESC;Voce tambem pode gravar de volta o valor normalizado, se quiser dados limpos em repouso:
-- One-off cleanup of inconsistent name casing UPDATE users SET name = initcap(name) WHERE name <> initcap(name);Onde o INITCAP quebra
A grande armadilha e que o
INITCAPtrata qualquer caractere nao alfanumerico como limite de palavra. Para nomes reais isso costuma estar errado:McDonald) se perdem: caem para minuscula.SELECT initcap('o''brien'); -- O'Brien (B capitalized after the quote) SELECT initcap('jean-luc PICARD'); -- Jean-Luc Picard SELECT initcap('mcdonald'); -- Mcdonald (not McDonald)A segunda sutileza e o locale e o Unicode. O PostgreSQL muda a caixa conforme o locale do banco, e para alguns pares (o
i/Iturco, por exemplo) o resultado pode diferir do esperado. Nomes ASCII sao previsiveis, mas com dados multilingues verifique o comportamento no seu proprio locale.Se o
INITCAPso estraga algumas linhas, nao grave o resultado na tabela inteira: selecione as linhas problematicas e corrija o resto no lugar:-- Apply initcap only where it is safe: skip names with apostrophes or hyphens UPDATE users SET name = initcap(name) WHERE name <> initcap(name) AND name !~ '[''-]';INITCAP no MySQL: emulacao
O MySQL nao tem
INITCAP. Para uma unica palavra e facil montar comUPPER,LOWEReSUBSTRING:-- MySQL: capitalize only the first letter of a single word SELECT CONCAT( UPPER(SUBSTRING(name, 1, 1)), LOWER(SUBSTRING(name, 2)) ) AS display_name FROM users;Para strings de varias palavras essa expressao nao basta: e preciso processar cada palavra. Na pratica os times resolvem com uma funcao armazenada que percorre a string e coloca em maiuscula a letra depois de cada espaco, ou empurram a logica para a camada de aplicacao. O ClickHouse tambem nao tem
INITCAP, mas ofereceupperUTF8,lowerUTF8esubstring, com os quais se constroi a mesma emulacao.Resumo: no PostgreSQL o
INITCAPe otimo para normalizar rapido strings ASCII simples, mas nao confie nele para sobrenomes com apostrofos, hifens ou maiusculas internas; no MySQL e no ClickHouse voce obtem o mesmo efeito na mao comUPPER+LOWER+SUBSTRING.