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.
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