.sqlfluff конфиг для dbt: rules, исключения, шаблон
В предыдущем уроке вы поняли, что sqlfluff с templater=dbt — это must-have для dbt-проектов. Теперь — детальный разбор .sqlfluff файла: какие правила выбрать, как селективно исключать, как настраивать под dbt-специфику.
Цель урока — собрать production-ready .sqlfluff, который команда будет реально использовать (а не отключать через --no-verify).
Структура .sqlfluff файла
.sqlfluff — это INI-файл с секциями. Базовая структура:
# Глобальные настройки
[sqlfluff]
dialect = duckdb
templater = dbt
rules = ... # или exclude_rules
max_line_length = 120
# Настройки templater
[sqlfluff:templater:dbt]
profiles_dir = ~/.dbt
profile = jaffle_shop
target = dev
# Настройки конкретных правил
[sqlfluff:rules:capitalisation.keywords]
capitalisation_policy = upper
[sqlfluff:rules:layout.indent]
indent_unit = space
tab_space_size = 4
Секции [sqlfluff:rules:CATEGORY.NAME] соответствуют конкретным правилам. Найти название секции для правила можно через sqlfluff rules или в документации.
Выбор правил: rules vs exclude_rules
Два подхода:
| Подход | Команда | Когда |
|---|---|---|
| Whitelist | rules = LT01, LT02, CV02 | Включить только эти правила. Хорошо для постепенного onboarding в legacy проект — начали с 3 правил, через месяц добавили ещё 3. |
| Blacklist | exclude_rules = AL05, ST05 | Включить все правила кроме этих. Хорошо для нового проекта, который хочет максимум линтинга. |
В типичном dbt проекте используют blacklist (или вообще не указывают — тогда все правила включены).
Топ правила, которые часто исключают
| Rule | Что проверяет | Почему часто исключают |
|---|---|---|
AM04 | SELECT * без alias | Часто используется в стейджинге SELECT * from source — это паттерн, не bug. |
AL05 | Tables aliased but не used | Иногда оставляем aliases для будущего изменения. False positive в Jinja-блоках. |
RF02 | Unqualified references в JOIN | Может быть строгим — все колонки требуют префикс таблицы. |
ST05 | Subqueries | Иногда subquery читаемее CTE. |
ST06 | Column order: literals -> expressions -> aggregates | Странное правило, не все принимают. |
LT05 | Line too long | Если используете sqlfmt — sqlfmt сам решает переносы. |
CP01 | Capitalisation keywords | Если используете sqlfmt — sqlfmt делает lowercase. |
Минимальный production .sqlfluff для dbt + DuckDB
# .sqlfluff — корень репо
[sqlfluff]
dialect = duckdb
templater = dbt
max_line_length = 120
# Если используете sqlfmt — exclude layout/capitalisation правила
exclude_rules =
LT01, LT02, LT05, LT07, LT09, LT10, LT12, LT13,
CP01,
AM04,
ST05
# Производительность
processes = 4
runaway_limit = 10
# Какие пути линтить
include = models/, snapshots/, analyses/, macros/
# Файлы исключить
exclude =
target/,
dbt_packages/,
logs/
[sqlfluff:indentation]
indent_unit = space
tab_space_size = 4
indented_joins = false
indented_using_on = true
template_blocks_indent = false
[sqlfluff:templater:dbt]
profiles_dir = ~/.dbt
profile = jaffle_shop
target = dev
[sqlfluff:rules:capitalisation.keywords]
capitalisation_policy = lower
[sqlfluff:rules:capitalisation.identifiers]
capitalisation_policy = lower
[sqlfluff:rules:capitalisation.literals]
capitalisation_policy = lower
[sqlfluff:rules:capitalisation.functions]
extended_capitalisation_policy = lower
[sqlfluff:rules:aliasing.column]
aliasing = explicit
[sqlfluff:rules:aliasing.table]
aliasing = explicit
[sqlfluff:rules:aliasing.length]
min_alias_length = 1
max_alias_length = 50
[sqlfluff:rules:layout.long_lines]
ignore_comment_lines = true
ignore_comment_clauses = true
[sqlfluff:rules:references.consistent]
force_enable = true
Разбор ключевых секций:
[sqlfluff:indentation]—template_blocks_indent = falseважен для dbt. Еслиtrue, sqlfluff требует индентить SQL внутри Jinja блоков, что часто конфликтует с естественным dbt-стилем.[sqlfluff:rules:capitalisation.*]— все lowercase (совместимо с sqlfmt). Если не используете sqlfmt, ставьтеupperдля keywords.[sqlfluff:rules:aliasing.column/table]—explicitозначает: все aliases должны быть черезAS. Запрещает имплицитный alias (SELECT id customer_idбез AS).
.sqlfluffignore: какие файлы пропустить целиком
Аналог .gitignore. Patterns relative к корню проекта.
# .sqlfluffignore
# Сгенерированный код
target/
dbt_packages/
# Тесты с raw SQL
tests/generic/*.sql
# Legacy модели (пока не успели почистить)
models/legacy/
# Конкретный файл с известной проблемой
models/marts/giant_pivot_table.sql
sqlfluff будет молча скипать эти пути. Удобно для миграции легаси: добавили в ignore, потихоньку чистите, когда готово — убрали из ignore.
Inline noqa: точечные исключения
В коде можно отключить конкретное правило для конкретной строки или блока:
Одна строка
SELECT * FROM {{ ref('stg_customers') }} -- noqa: AM04
WHERE created_at > '2026-01-01'
-- noqa: AM04 отключит правило AM04 (SELECT *) для этой строки.
Несколько правил
SELECT *, NOW() current_time -- noqa: AM04, AL01
FROM {{ ref('stg_customers') }}
Блок
-- noqa: disable=AM04
SELECT * FROM table_a;
SELECT * FROM table_b;
-- noqa: enable=AM04
disable отключает с этой строки, enable включает обратно.
Отключить все правила в файле
-- noqa: disable=all
SELECT 1; SELECT 2; SELECT 3;
Не злоупотребляйте -- noqa. Это спасение от изолированных случаев, не способ обойти правило, которое команде не нравится. Если правило часто требует -- noqa, обсудите его в .sqlfluff (отключить или настроить мягче).
Иерархия конфигов
sqlfluff ищет .sqlfluff по иерархии: текущий файл -> родительские директории -> корень проекта -> ~/.sqlfluff. Каждый уровень переопределяет предыдущий.
Это удобно для дифференцированных правил:
.
├── .sqlfluff # глобальный конфиг
├── models/
│ ├── staging/
│ │ └── .sqlfluff # более мягкие правила в staging
│ └── marts/
│ └── .sqlfluff # строже в marts
└── analyses/
└── .sqlfluff # ещё мягче — это ad-hoc SQL
Пример сценария:
# models/staging/.sqlfluff — более мягкий
[sqlfluff]
exclude_rules = AM04, AL05, RF02 # SELECT * допустим в стейджинге
# models/marts/.sqlfluff — строгий
[sqlfluff]
rules = all
exclude_rules = # минимум исключений
[sqlfluff:rules:references.qualification]
strict = true
Это говорит «в staging слое прощаем многое, в marts всё строго». Реалистично для production проекта.
Кастомные macros для sqlfluff
dbt-проект использует кастомные macros ({{ generate_schema_name(...) }}, {{ my_custom_macro(...) }}). Чтобы sqlfluff их понимал через templater=dbt, нужно чтобы они скомпилировались — то есть dbt compile должен пройти.
Проблема: некоторые macros используют var() или env_var(), которых нет в CI:
{% set start_date = var('start_date', '2026-01-01') %}
Если start_date не задан и default отсутствует — dbt compile падает, и sqlfluff не работает.
Решение: всегда задавать defaults в var()/env_var(), либо использовать dbt_project.yml:
# dbt_project.yml
vars:
start_date: '2026-01-01'
Теперь var('start_date') всегда резолвится, sqlfluff проходит.
Performance: ускорения
Sqlfluff с templater=dbt может быть медленным (10-60 сек на средний проект). Ускорения:
1. Параллелизация
[sqlfluff]
processes = 4 # или 0 = auto-detect
Используйте processes = 0 чтобы sqlfluff сам определил количество ядер.
2. Используйте готовый manifest.json
Если перед линтом был dbt parse (создал manifest), sqlfluff его подцепит и не будет перепарсивать:
dbt deps
dbt parse # создаёт target/manifest.json
sqlfluff lint # использует существующий manifest, не парсит заново
В CI это сильно ускоряет (с 60 сек до 5).
3. Линтить только изменённые файлы
# Только staged файлы
sqlfluff lint $(git diff --cached --name-only --diff-filter=ACM | grep '\.sql$')
# Только файлы из текущего PR
sqlfluff lint $(git diff origin/main...HEAD --name-only | grep '\.sql$')
В pre-commit это делается автоматически (pre-commit передаёт hook только staged файлы).
4. Кеш sqlfluff
sqlfluff lint --cache-key v1
Кеширует AST. На повторных запусках — быстрее.
Запуск через pre-commit
В .pre-commit-config.yaml:
repos:
- repo: https://github.com/sqlfluff/sqlfluff
rev: 3.4.0
hooks:
- id: sqlfluff-lint
args:
- --dialect=duckdb
- --templater=dbt
- --processes=0
additional_dependencies:
- dbt-duckdb==1.10.0
- sqlfluff-templater-dbt==3.4.0
files: \.(sql|sql.jinja)$
- id: sqlfluff-fix
args:
- --dialect=duckdb
- --templater=dbt
additional_dependencies:
- dbt-duckdb==1.10.0
- sqlfluff-templater-dbt==3.4.0
files: \.(sql|sql.jinja)$
additional_dependencies — критично. pre-commit ставит sqlfluff в изолированный venv, и без этого там не будет ни dbt-duckdb, ни templater. На каждом коммите hook запускается в этом venv -> нужны все deps.
Без additional_dependencies pre-commit запустит sqlfluff в изолированном окружении без dbt-duckdb. Получите ошибку ‘Could not find dbt-duckdb adapter’ и потратите час на диагностику. Всегда указывайте все runtime deps для templater=dbt.
Запуск в CI (GitHub Actions)
Минимальный сниппет (полный пайплайн будет в следующем модуле):
# .github/workflows/lint.yml
name: SQL Lint
on:
pull_request:
paths:
- 'models/**/*.sql'
- '.sqlfluff'
jobs:
sqlfluff:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install deps
run: |
pip install dbt-duckdb==1.10.0 sqlfluff==3.4.0 sqlfluff-templater-dbt==3.4.0
- name: dbt deps + parse
run: |
dbt deps
dbt parse --profiles-dir .
env:
DBT_PROFILES_DIR: ${'{{'} github.workspace {'}}'}
- name: Sqlfluff lint
run: sqlfluff lint models/
Здесь dbt parse создаёт target/manifest.json, на основе которого sqlfluff линтит — без полного compile.
Попробуй сам
В вашем dbt-проекте:
- Создайте
.sqlfluffсо следующим:
[sqlfluff]
dialect = duckdb
templater = dbt
exclude_rules = LT01, LT05, CP01, AM04
[sqlfluff:templater:dbt]
profiles_dir = ~/.dbt
profile = jaffle_shop
target = dev
- Создайте файл
models/staging/stg_bad.sql:
SELECT id customer_id, name, status
FROM {{ ref('stg_customers') }}
WHERE created_at > '2026-01-01'
- Запустите:
dbt parse
sqlfluff lint models/staging/stg_bad.sql
Получите ошибку про aliasing (нет AS).
- Добавьте
-- noqa: AL01:
SELECT id customer_id, name, status -- noqa: AL01
FROM {{ ref('stg_customers') }}
Снова sqlfluff lint — теперь чисто.
- Уберите
-- noqa, исправьте наid AS customer_id. Lint должен пройти.
Бонус: создайте models/staging/.sqlfluff с более мягким exclude_rules (добавив AL01) — увидите как иерархия конфигов работает.
Ключевые выводы
.sqlfluff— INI-файл в корне репо. Секции: глобальная[sqlfluff], templater[sqlfluff:templater:dbt], конкретные правила[sqlfluff:rules:CATEGORY.NAME].- Выбор правил: whitelist (
rules = ...) или blacklist (exclude_rules = ...). В типичном dbt — blacklist с 5-10 исключениями. - Топ исключаемые правила:
AM04(SELECT *),AL05,RF02,ST05,LT05/CP01(если sqlfmt используется). - Inline noqa для точечных исключений:
-- noqa: RULE_ID. Не злоупотреблять — если часто, обсудить в.sqlfluff. - Иерархия конфигов: разные правила для
staging/,marts/,analyses/. Локальный.sqlfluffпереопределяет родительский. - Performance:
processes=0, использование готовогоtarget/manifest.jsonчерезdbt parse, кеш--cache-key. - pre-commit интеграция:
additional_dependenciesобязательны (dbt-duckdb, sqlfluff-templater-dbt) для изолированного venv pre-commit.