Automatizando a formatação de códigos SQL com SQLFluff.

Como configuramos e aplicamos a ferramenta SQLFluff para facilitar a rotina de criação de modelos no DBT.

Alice Thomaz
8 min readJul 18, 2023

Durante a criação do nosso projeto no DBT, estabelecemos diversas diretrizes de desenvolvimento de códigos SQL, incluindo a formatação padrão a ser utilizada. Inicialmente, enfrentamos alguns desafios nesse processo devido à ausência de um padrão específico no mercado e à resistência em relação ao tempo gasto na formatação, uma vez que cada pessoa seguia seu próprio estilo baseado em preferências pessoais.

Nossa intenção ao definir um padrão de formatação para o novo projeto era facilitar ao máximo a leitura e a manutenção dos nossos códigos, uma área em que nosso projeto anterior deixava a desejar. Durante a migração em massa das modelagens SQL analíticas, percebemos que a falta desse padrão tornou o processo mais trabalhoso. No entanto, também não queríamos comprometer o prazo dos projetos devido a isso, então decidimos realizar um período de discovery para identificar a ferramenta mais adequada às nossas necessidades.

Foi assim que encontramos o SQLFluff, um linter SQL personalizável com um dialeto flexível, que nos permitiu automatizar as tarefas do dia a dia e adapta-la amplamente à nossa realidade. As configurações abaixo foram estabelecidas em colaboração entre as equipes de engenharia e análise de dados, podendo ser ajustadas de acordo com a realidade da sua empresa. Embora nem todos os pontos desejados tenham sido contemplados, conseguimos a maioria, o que já facilitou bastante esse processo.

>> Arquivo de configuração

Será preciso criar um arquivo chamado .sqlfluff no seu projeto para definir as configurações principais da ferramenta. Optamos por personalizar o máximo possível de acordo com a nossa realidade, mas a ferramenta em si já possui uma padronização default que pode ser utilizada. Abaixo está a versão final do nosso arquivo como exemplo, e em seguida explicarei cada seção do arquivo.

[sqlfluff]
templater = jinja
dialect = redshift
exclude_rules = AL04, AL05, AL06, AL07, AM01, AM02, AM03, AM04, AM07, CV01,
CV02, CV03, CV04, CV05, CV06, CV07, CV08, CV09, CV10, CV11,
LT03, LT05, LT07, LT08, LT12, LT13, RF01, RF02, RF03, RF04,
RF05, RF06, ST01, ST02, ST03, ST06, ST07, ST08, TQ01
ignore = templating
large_file_skip_byte_limit = 0
max_line_length = 0


[sqlfluff:layout:type:alias_expression]
spacing_before = align
align_within = select_clause
spacing_after = touch

[sqlfluff:indentation]
tab_space_size = 2
indent_unit = space
indented_joins = false
indented_using_on = true
allow_implicit_indents = true
indented_on_contents = false
indented_ctes = false

[sqlfluff:rules:aliasing.table]
aliasing.table = explicit

[sqlfluff:rules:aliasing.column]
aliasing.column = explicit

[sqlfluff:rules:aliasing.expression]
allow_scalar = True

[sqlfluff:rules:ambiguous.join]
fully_qualify_join_types = inner

[sqlfluff:rules:ambiguous.column_references]
group_by_and_order_by_style = consistent

[sqlfluff:rules:capitalisation.keywords]
capitalisation_policy = upper

[sqlfluff:rules:capitalisation.identifiers]
extended_capitalisation_policy = lower
unquoted_identifiers_policy = all

[sqlfluff:rules:capitalisation.functions]
extended_capitalisation_policy = upper

[sqlfluff:rules:capitalisation.literals]
capitalisation_policy = upper

[sqlfluff:rules:capitalisation.types]
extended_capitalisation_policy = upper

[sqlfluff:rules:jinja.padding]
single_space = true

[sqlfluff:rules:layout.spacing]
no_trailing_whitespace = true
extra_whitespace = false

[sqlfluff:rules:layout.commas]
line_position = trailing

[sqlfluff:rules:layout.functions]
no_space_after_function_name = true

[sqlfluff:rules:layout.select_targets]
wildcard_policy = single

[sqlfluff:rules:layout.set_operators]
set_operator_on_new_line = ['UNION', 'UNION ALL']

[sqlfluff:rules:structure.nested_case]

[sqlfluff:rules:structure.subquery]
forbid_subquery_in = join

Configuração inicial — [sqlfluff]

No início do projeto, iremos estabelecer as configurações iniciais do arquivo:

  • templater: A ferramenta atualmente oferece alguns modelos, como raw, jinja, python e placeholder. Embora seja possível usar o modelo do dbt com uma instalação diferente, o Fluff recomenda o uso do jinja nesse caso.
  • dialect: Será definido de acordo com o banco de dados utilizado. Para verificar quais dialetos são suportados atualmente, consulte: Dialects Reference.
  • exclude_rules: Como algumas rules não se adequavam ao que desejávamos, decidimos desativá-las. No próximo tópico, explicaremos as rules com mais detalhes.
  • ignore: Essas rules são agrupadas por grupos. Neste caso, optamos por ignorar o templating, pois ele não funciona bem com as configurações jinja iniciais que definimos em nossos modelos no DBT.
  • large_file_skip_byte_limit: Por padrão, o Fluff verifica apenas códigos com até 20 mil bytes. Ao definir o valor como 0, podemos desabilitar essa função.
  • max_line_length: Seguindo a mesma linha, o Fluff define um limite máximo de tamanho de linha de 80 por padrão. Definimos o valor como 0 para desativá-lo.

Rules

As rules separam os padrões de formatação por tema, a ferramenta já tem algumas configurações próprias, mas é possível personalizá-las separadamente. Abaixo estão os padrões que usamos e para mais detalhes sobre cada uma, só seguir na documentação oficial: Rules Reference.

  • [sqlfluff:layout:type:alias_expression]: Essa rule é responsável por alinhar todos os ‘AS’ para colunas renomeadas no SELECT.
  • [sqlfluff:indentation]: Aqui você define os espaçamentos usados para indentação, por padrão trabalhamos com 2 espaços de distância.
  • [sqlfluff:rules:aliasing.table]: Definimos que quando a tabela for renomeada no FROM, é necessário ter o ‘AS’ explícito. Porém, também há a opção ‘implicit’.
  • [sqlfluff:rules:aliasing.column]: Definimos que quando a coluna for renomeada no SELECT, é necessário ter o ‘AS’ explícito. Porém, também há a opção ‘implicit’.
  • [sqlfluff:rules:aliasing.expression]: Definimos que as alterações em colunas devem ser renomeadas, exemplo: SUM(events) AS total_events. No entanto, a cláusula pode ser False.
  • [sqlfluff:rules:ambiguous.join]: Quando for usado o JOIN sem identificar se é LEFT, RIGHT, etc., ele será considerado INNER. Porém, também há as opções ‘outer’ e ‘both’.
  • [sqlfluff:rules:ambiguous.column_references]: Ao configurar o GROUP BY e ORDER BY, as colunas devem ser constantes, ou seja, todos números ou todos nomes. Também é possível definir a opção para um único tipo ‘implicit’ — tudo com número — ou ‘explicit’ — sempre como nome.
  • [sqlfluff:rules:capitalisation.keywords]: Todas as palavras chaves em maiúsculo, exemplo: SELECT, FROM, UNION. Também é possível definir outras opções como ‘consistent’, ‘lower’ e ‘capitalise’, além de escolher ignorar algumas palavras com ‘ignore_words’.
  • [sqlfluff:rules:capitalisation.identifiers]: Todos os nomes das colunas em minúsculo. Podendo usar também ‘consistent’, ‘upper’, ‘pascal’ e ‘capitalise’.
  • [sqlfluff:rules:capitalisation.functions]: Todas as funções em maiúsculo, exemplo: SUM, MAX, MIN. Também é possível definir outras opções como ‘consistent’, ‘lower’, ‘pascal’ e ‘capitalise’, além de escolher ignorar algumas palavras com ‘ignore_words’.
  • [sqlfluff:rules:capitalisation.literals]: Todos os valores fixos em maiúsculo, exemplo: NULL, TRUE, FALSE.
  • [sqlfluff:rules:capitalisation.types]: Todos os tipos de dados em maiúsculo, exemplo: INT, VARCHAR. Também é possível definir outras opções como ‘consistent’, ‘lower’, ‘pascal’ e ‘capitalise’, além de escolher ignorar algumas palavras com ‘ignore_words’.
  • [sqlfluff:rules:jinja.padding]: Deixar apenas 1 espaço entre delimitadores Jinja, exemplo: {{ ref(‘table’) }}.
  • [sqlfluff:rules:layout.spacing]: Lida com a remoção de espaços em branco no final das linhas com a condição ‘no_trailing_whitespace = true’, além de no caso de haver espaços em branco extras removê-los com a condição ‘extra_whitespace = false’.
  • [sqlfluff:rules:layout.commas]: Define a posição da vírgula, aqui usamos a vírgula na direita com ‘trailing’ mas pode ser à esquerda com ‘leading’.
  • [sqlfluff:rules:layout.functions]: Remove o espaço entre a função e os parênteses, exemplo: SUM(column).
  • [sqlfluff:rules:layout.select_targets]: Definimos que se só estiver chamando 1 coluna na CTE, deixamos na mesma linha do SELECT, para múltiplas colunas pulamos linha.
    Também é possível definir que essa linha sempre seja pulada independente da quantidade de colunas, usando ao invés da opção ‘single’, a opção ‘multiple’.
  • [sqlfluff:rules:layout.set_operators]: O UNION e UNION ALL devem estar isolados em uma quebra de linha, exemplo:
SELECT * FROM tabela
UNION
SELECT * FROM tabela2
  • [sqlfluff:rules:structure.nested_case]: Realizar o tratamento da função CASE WHEN, exemplo:
CASE 
WHEN
ELSE CASE
WHEN

VVVV

CASE
WHEN
WHEN
END
  • [sqlfluff:rules:structure.subquery]: Retira a subquery de dentro do JOIN e transforma em duas CTEs distintas, exemplo: JOIN (SELECT x, z FROM b). Também é possível fazer isso com subquery dentro do FROM, com as opções ‘from’ e ‘both’.

Visão geral do código

A seguir, apresento um exemplo de uma consulta antes e após a correção via linha de comando. O resultado final dependerá das configurações que você definir de acordo com os padrões da empresa.

>> Como usar a ferramenta

Instalação

Para instalar, é necessário ter o pip e o Python já instalados no ambiente. Em seguida, execute o seguinte comando no terminal do VSCode. Eu recomendo especificar a versão para evitar conflitos com o arquivo de configuração, mas isso não é obrigatório:

pip install "sqlfluff==2.1.2"

Executar Fix

O comando fix é responsável por identificar divergências e corrigir automaticamente a formatação do código. Para usar, siga as etapas abaixo:

  • Primeiro, copie o caminho do código. Para fazer isso, clique com o botão direito no arquivo e selecione “Copy Relative Path”.
  • Em seguida, abra o terminal dentro do VSCode e execute o comando a seguir, substituindo “pasta/modelo.sql” pelo caminho copiado na etapa anterior:
sqlfluff fix pasta/modelo.sql 
  • O comando listará todas as inconformidades, indicando a linha (ex: L 11) e uma descrição do problema de formatação.
  • Além disso, você será solicitado a confirmar se deseja aplicar as correções. Digite “Y” e pressione Enter para iniciar os ajustes.

Analisar formatação

O comando lint é responsável por identificar e listar as divergências na formatação do código, sem fazer correções automáticas. Para usar, siga as etapas abaixo:

  • Primeiro, copie o caminho do código. Para fazer isso, clique com o botão direito no arquivo e selecione “Copy Relative Path”.
  • Em seguida, abra o terminal dentro do VSCode e execute o comando a seguir, substituindo “pasta/modelo.sql” pelo caminho copiado na etapa anterior:
sqlfluff lint pasta/modelo.sql 
  • Visão:

— Em caso de falha: Serão listadas todas as inconformidades encontradas, indicando a linha (ex: L 21) e uma descrição do problema.

Atenção: Algumas rules, como aquela que exige a renomeação de colunas alteradas, são exibidas apenas como avisos durante o lint e precisam ser ajustadas manualmente. Enquanto que outras regras como as relacionadas a espaçamento (LT01 e LT02) podem ser ignoradas.
Para falhas não corrigidas pelo comando fix, a recomendação é ir até a linha correspondente e verificar se a alteração é realmente necessária. Caso contrário, pode ser ignorada pois costumam ser poucos casos em exceção.

Em caso de sucesso:

O SQLFluff desempenhou um papel crucial na otimização do tempo de desenvolvimento de novas modelagens no DBT, ao mesmo tempo em que mitigou reclamações internas sobre qual seria o padrão ideal. Dado que não existe um padrão de mercado estabelecido para códigos SQL, a capacidade do Fluff de nos permitir personalizar cada aspecto desse processo foi um diferencial significativo.

No entanto, o processo de personalização das configurações não é fácil. A documentação oficial muitas vezes omite informações ou apresenta condições incorretas, portanto, é necessário ter cuidado ao personalizar o arquivo. Recomendo testar gradualmente à medida que novas cláusulas são adicionadas. Além disso, se você tiver mais dúvidas, sugestões ou comentários sobre o conteúdo, sinta-se à vontade para entrar em contato comigo por meio do Linkedin.

English version: https://medium.com/@alice_thomaz/4723779f19c6

--

--