Pular para a ferramenta
makeshortwork.com Formatador de SQL

Formatador de SQL

Cole a consulta, escolha o dialeto e leia o SQL indentado, com subconsultas e CTEs aninhadas onde deveriam estar. Nada é enviado — o formatador roda nesta aba.

SQL formatado
 

Roda inteiro nesta aba — sua consulta nunca é enviada.

SQL malformatado é problema de revisão, não de gosto

SQL é a última parte da maioria dos repositórios em que ainda se discute formatação em vez de automatizá-la. Código de aplicação passa por Prettier, gofmt, Black ou rustfmt antes de virar pull request. Já a consulta enfiada três níveis abaixo num arquivo, ou a migração que alguém colou direto do cliente do banco, chega do jeito que foi digitada: uma linha de 900 caracteres, ou quarenta linhas indentadas do jeito que o editor achou melhor.

A conta aparece na revisão. Quem lê uma consulta sem formatação não enxerga o formato dela — quais condições pertencem ao ON e quais ao WHERE, se aquele OR está dentro ou fora dos parênteses, quantas tabelas realmente entram no join. E são exatamente esses os erros que produzem uma consulta que roda, devolve linhas plausíveis e está errada. Um OR que escapou dos parênteses não levanta exceção nenhuma: ele devolve a tabela inteira, calado.

Formatação consistente também devolve utilidade ao controle de versão. Quando toda consulta do repositório quebra linha nos mesmos lugares, mudar uma condição gera um diff de uma linha. Quando não quebra, acrescentar uma coluna ao SELECT reescreve a instrução inteira no diff, e quem revisa precisa reler tudo para descobrir o que mudou. Esse é o argumento prático mais forte para passar SQL por um formatador — não a beleza, e sim o tamanho do diff.

As convenções de estilo que brigam entre si

Não existe guia de estilo oficial para SQL, então cada time herda a convenção de quem escreveu a primeira migração. Quatro discussões se repetem em todo lugar, e vale entender o que cada lado está de fato otimizando.

EscolhaArgumento a favorArgumento contra
Palavra-chave em maiúscula Separa palavra reservada de identificador sem depender de destaque de sintaxe — num diff, num log, num terminal Parece grito; irrelevante se o editor já colore
Vírgula à frente Comentar ou apagar uma linha nunca quebra a linha de cima Estranha para quem aprendeu do outro jeito
Alinhamento em coluna (rio) Separa visualmente a coluna de palavras-chave da coluna de valores Cada renomeação realinha o bloco inteiro e incha o diff
Uma condição por linha Cada predicado vira uma linha independente e revisável Consulta curta fica alta sem ganho nenhum

O argumento da vírgula à frente é o mais subestimado, então vale destrinchar. Com vírgula ao fim, o último item da lista não tem vírgula. Comente essa última linha e a linha de cima passa a terminar com vírgula solta, o que é erro de sintaxe na maioria dos bancos: para remover uma linha você teve que editar duas. Com vírgula à frente, a vírgula pertence ao item que vem depois dela, então toda linha se basta — comente qualquer uma, inclusive a última, e a lista restante continua válida. Quando você está caçando o problema de um relatório removendo coluna por coluna, essa diferença é o jogo inteiro.

O contra-argumento também é real: vírgula à frente parece errada até deixar de parecer, e misturar os dois estilos no mesmo repositório é pior que qualquer um dos dois. Por isso a posição da vírgula aqui é uma chave, e não uma opinião embutida na saída — escolha a que seu time já usa e o formatador deixa de ser fonte de ruído no diff.

Diferenças de dialeto que mudam a formatação

A maior parte das palavras-chave é comum a todos os bancos. O que não é comum é como cada um delimita as coisas — e delimitador é exatamente o que um formatador precisa acertar. Uma suposição errada transforma nome de coluna em string, ou string em código.

Como este formatador evita corromper a sua consulta

Todo bug de formatador que importa tem a mesma forma: alguma coisa dentro de um literal foi tratada como código. O relato clássico é um WHERE observacao = 'SELECT FROM' que volta reformatado, com a caixa trocada ou quebrado em duas linhas — uma consulta que continua rodando e agora não casa com nada. Por isso a primeira coisa que acontece aqui é uma varredura completa de tokenização, antes de qualquer decisão de layout.

O tokenizador percorre o texto caractere a caractere com estado explícito. Dentro de uma string ele sabe que duas aspas seguidas são uma aspa escapada e não o fim do literal — que é exatamente o ponto em que implementações ingênuas terminam cedo e corrompem tudo dali em diante. Dentro de um comentário de linha ele sabe que um apóstrofo é só um apóstrofo. Dentro de um identificador delimitado ele sabe qual caractere de fechamento procurar, e isso depende do dialeto que você escolheu. Só tokens classificados como palavra sem delimitador podem ter a caixa alterada, e só se estiverem na lista de palavras reservadas.

Nome de função tem uma condição a mais: a caixa só muda quando vem um ( colado. Muito schema real tem coluna chamada count, replace ou position, e não há motivo para gritar com elas.

Duas consequências vêm de tratar isto como problema de segurança e não de renderização. A primeira: se o tokenizador não consegue terminar — string sem fechar, comentário de bloco aberto, identificador que nunca fecha — nada é formatado. Você recebe a entrada intacta e o motivo. Uma consulta formatada pela metade, com cara de pronta, é pior que saída nenhuma, porque convida você a copiar. A segunda: depois de produzido, o resultado é tokenizado de novo e comparado token a token com a entrada. Se algo se perdeu, duplicou ou foi reinterpretado, o resultado é descartado e o original volta. Formatar é conveniência; corromper uma consulta que depois roda em produção é prejuízo de verdade, e a troca não compensa.

Lendo a estrutura: CTE, subconsulta, JOIN … ON e CASE

A indentação se paga em quatro lugares, e é neles que a complexidade deste formatador foi gasta.

Subconsulta e CTE ganham um nível de aninhamento. Um parêntese cujo primeiro token relevante é SELECT ou WITH vira bloco: abre no fim da linha atual, o conteúdo desce um nível e o parêntese de fechamento fica sozinho na indentação da linha que o abriu. Parêntese de chamada de função e de lista IN (…) continua na mesma linha, porque quebrá-lo acrescenta altura sem acrescentar informação. É essa distinção que faz uma consulta com três CTEs ser legível de relance — dá para ver onde cada bloco começa e termina sem contar parênteses.

JOIN … ON coloca o join em linha própria no nível da cláusula e a condição um nível adiante. Quando o join tem várias condições, cada AND cai numa linha abaixo do ON, e fica imediatamente visível se um predicado pertence ao join ou ao WHERE — distinção que muda o resultado em outer join e que é invisível numa consulta de uma linha só.

Expressão CASE coloca cada WHEN e o ELSE em linha própria, com o END de volta ao nível do CASE. Um CASE de cinco ramos escrito em linha é ilegível; escrito assim, vira uma tabelinha.

Lista longa de colunas quebra um item por linha, mas só quando há mais de um item. ORDER BY criado_em DESC fica numa linha só; um SELECT de quinze colunas vira quinze linhas. Quebrar cláusula de item único é o tipo de regra que deixa o SQL formatado com o dobro da altura necessária.

O AND de BETWEEN … AND … não é tratado como conectivo lógico, de propósito, porque não é um: ele faz parte do operador. Formatador que quebra nele produz uma linha solta que se lê como se fosse outra condição.

Por que colar SQL de produção num site que faz upload é problema

A maioria dos formatadores online manda seu texto para um servidor, formata lá e devolve o resultado. Para um seletor de cores isso seria uma curiosidade. Para SQL, é divulgação de informação.

Uma consulta de produção é uma descrição compacta do seu sistema. Ela nomeia tabelas e colunas, o que já é vazamento de schema. Ela codifica regra de negócio — como um cliente é classificado, o que conta como cancelamento, qual flag suprime uma cobrança. E os literais do WHERE são, com frequência, dado vivo: um id de conta, um e-mail, um CPF que alguém estava investigando quando a consulta parou de funcionar. Essa combinação é o que uma revisão de segurança chama de vazamento de dado — e acontece por uma caixa de texto, sem arquivo, então nada no pipeline de DLP percebe.

A consequência prática não é que formatar seja perigoso, e sim que o lugar do processamento decide se é. Aqui o formatador é um módulo JavaScript entregue junto com a página. Não há código de rede dentro dele, e dá para conferir: abra a aba de rede, cole uma consulta, formate e veja que nada sai. Dá para carregar a página, desconectar e continuar formatando. Se uma ferramenta não sobrevive a esse teste, ela está enviando sua consulta, diga o que disser a página de privacidade.

O que esta ferramenta não faz, de propósito

Ser explícito sobre os limites é mais útil do que fingir que eles não existem:

Perguntas frequentes

Minha consulta é enviada para algum servidor?

Não. O tokenizador e o formatador são JavaScript que vem junto com a página e roda dentro da sua aba. Não há upload, nem chamada de API, nem registro. Isso pesa mais em SQL do que em qualquer outro formato: uma consulta expõe nomes de tabela, nomes de coluna e boa parte da regra de negócio, e costuma carregar dado real de cliente dentro do WHERE.

A formatação pode mudar o que a consulta faz?

O desenho é justamente para que não mude. A formatação só reescreve espaço em branco e a caixa das palavras reservadas; conteúdo de string, de comentário e de identificador delimitado é copiado byte a byte. Antes de mostrar qualquer coisa, a ferramenta tokeniza a própria saída e compara token a token com a entrada. Se um único token divergir, o resultado é descartado e o SQL original volta com uma explicação.

E se meu SQL tiver erro de sintaxe?

Depende do tipo de erro. Isto é um formatador, não um parser com gramática: um GROUP BY errado ou um alias faltando continuam sendo formatados, porque a ferramenta não tem como saber que a consulta está errada. O que ela recusa é o que não consegue tokenizar com segurança — string sem fechar, comentário de bloco aberto, identificador cujo delimitador nunca fecha. Nesses casos nada é alterado e você recebe o motivo.

Qual dialeto eu devo escolher?

O do banco em que a consulta realmente roda. A opção não é cosmética: ela decide o que significa aspas duplas (identificador em todo lugar, menos no MySQL padrão, onde é string), se crase e colchete delimitam identificador, se # começa comentário e se existe string com cifrão. Escolher o dialeto errado é a maneira mais rápida de um formatador estragar um literal.

Por que o Minificar mantém uma quebra de linha depois do comentário?

Porque o comentário -- vale até o fim da linha. Colapsar numa linha só uma consulta que tem comentário de linha comentaria tudo o que vem depois dele, transformando uma instrução que funciona numa que não roda. Por isso o minificador mantém exatamente uma quebra depois de cada comentário de linha e remove todas as outras. Comentário de bloco não tem esse problema e continua embutido.