Learning Platform
Глоссарий Troubleshooting
Урок 13.03 · 30 мин
Средний
sqlfluffConfigurationRulesnoqadbt templater

.sqlfluff конфиг для dbt: rules, исключения, шаблон

В предыдущем уроке вы поняли, что sqlfluff с templater=dbt — это must-have для dbt-проектов. Теперь — детальный разбор .sqlfluff файла: какие правила выбрать, как селективно исключать, как настраивать под dbt-специфику.

Цель урока — собрать production-ready .sqlfluff, который команда будет реально использовать (а не отключать через --no-verify).

dbt-iii: Jinja parse фаза — почему templater=dbt обязателен

Структура .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

Два подхода:

ПодходКомандаКогда
Whitelistrules = LT01, LT02, CV02Включить только эти правила. Хорошо для постепенного onboarding в legacy проект — начали с 3 правил, через месяц добавили ещё 3.
Blacklistexclude_rules = AL05, ST05Включить все правила кроме этих. Хорошо для нового проекта, который хочет максимум линтинга.

В типичном dbt проекте используют blacklist (или вообще не указывают — тогда все правила включены).

Топ правила, которые часто исключают

RuleЧто проверяетПочему часто исключают
AM04SELECT * без aliasЧасто используется в стейджинге SELECT * from source — это паттерн, не bug.
AL05Tables aliased but не usedИногда оставляем aliases для будущего изменения. False positive в Jinja-блоках.
RF02Unqualified references в JOINМожет быть строгим — все колонки требуют префикс таблицы.
ST05SubqueriesИногда subquery читаемее CTE.
ST06Column order: literals -> expressions -> aggregatesСтранное правило, не все принимают.
LT05Line too longЕсли используете sqlfmt — sqlfmt сам решает переносы.
CP01Capitalisation 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;
WARNING

Не злоупотребляйте -- 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.

DANGER

Без 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-проекте:

  1. Создайте .sqlfluff со следующим:
[sqlfluff]
dialect = duckdb
templater = dbt
exclude_rules = LT01, LT05, CP01, AM04

[sqlfluff:templater:dbt]
profiles_dir = ~/.dbt
profile = jaffle_shop
target = dev
  1. Создайте файл models/staging/stg_bad.sql:
SELECT id customer_id, name, status
FROM {{ ref('stg_customers') }}
WHERE created_at > '2026-01-01'
  1. Запустите:
dbt parse
sqlfluff lint models/staging/stg_bad.sql

Получите ошибку про aliasing (нет AS).

  1. Добавьте -- noqa: AL01:
SELECT id customer_id, name, status  -- noqa: AL01
FROM {{ ref('stg_customers') }}

Снова sqlfluff lint — теперь чисто.

  1. Уберите -- noqa, исправьте на id AS customer_id. Lint должен пройти.

Бонус: создайте models/staging/.sqlfluff с более мягким exclude_rules (добавив AL01) — увидите как иерархия конфигов работает.


Ключевые выводы

  1. .sqlfluff — INI-файл в корне репо. Секции: глобальная [sqlfluff], templater [sqlfluff:templater:dbt], конкретные правила [sqlfluff:rules:CATEGORY.NAME].
  2. Выбор правил: whitelist (rules = ...) или blacklist (exclude_rules = ...). В типичном dbt — blacklist с 5-10 исключениями.
  3. Топ исключаемые правила: AM04 (SELECT *), AL05, RF02, ST05, LT05/CP01 (если sqlfmt используется).
  4. Inline noqa для точечных исключений: -- noqa: RULE_ID. Не злоупотреблять — если часто, обсудить в .sqlfluff.
  5. Иерархия конфигов: разные правила для staging/, marts/, analyses/. Локальный .sqlfluff переопределяет родительский.
  6. Performance: processes=0, использование готового target/manifest.json через dbt parse, кеш --cache-key.
  7. pre-commit интеграция: additional_dependencies обязательны (dbt-duckdb, sqlfluff-templater-dbt) для изолированного venv pre-commit.
Проверка знанийKnowledge check
В команде sqlfluff падает на каждом PR с false positive: правило ST06 (column order: literals -> expressions -> aggregates) ругается на естественный порядок колонок 'id первым'. Один разработчик пишет \`-- noqa: ST06\` в каждой модели. Что не так с этим подходом и как правильно?
ОтветAnswer
**Проблемы с `-- noqa` в каждой модели:**\n\n1. **Шум.** Каждая модель содержит технический комментарий, который не несёт смысла для бизнеса.\n2. **Косвенно сигналит**, что правило не подходит для команды, но это нигде не задокументировано.\n3. **Новые разработчики не знают**, что делать с ST06 — копируют `-- noqa` без понимания.\n\n**Правильный путь** — отключить правило централизованно в `.sqlfluff`:\n\n```ini\n[sqlfluff]\nexclude_rules = ST06\n```\n\nИли если правило в целом полезно, но мешает только в одном слое — иерархия конфигов:\n\n```ini\n# models/staging/.sqlfluff\n[sqlfluff]\nexclude_rules = ST06 # в стейджинге не нужно\n\n# models/marts/.sqlfluff остаётся без exclude\n```\n\n**Когда -- noqa оправдан:**\n\n- Единичный случай (1 модель из 100), где правило технически правильно срабатывает, но в этом конкретном месте есть веская причина для нарушения.\n- Временная мера во время рефакторинга legacy-модели.\n\n**Когда -- noqa антипаттерн:**\n\n- Появляется в каждой второй модели.\n- Используется чтобы обойти правило, которое команде не нравится.\n- Применяется без обсуждения в команде.\n\nГлавное — обсуждать на ретро/standup-е: «правило X у нас часто срабатывает, что делаем — оставляем, отключаем централизованно, или меняем стиль?». Это решение должно быть осознанным, а не индивидуальным.
Проверка знанийKnowledge check
В CI установили \`sqlfluff\` через \`pip install sqlfluff\` и запустили \`sqlfluff lint --templater dbt models/\`. Получили ошибку 'Could not load dbt templater. Please install sqlfluff-templater-dbt'. Установили его — теперь 'Could not find adapter dbt-duckdb'. Что не так и как починить правильно?
ОтветAnswer
Это **проблема несовместимости версий и неполных deps** в CI venv.\n\n**Что произошло:**\n\n1. `pip install sqlfluff` — поставил только базовый sqlfluff с jinja templater.\n2. `pip install sqlfluff-templater-dbt` — добавил dbt templater, но templater сам по себе не запускает dbt — он только парсит manifest.\n3. **Templater dbt запускает `dbt compile` или читает manifest.json**. Для этого нужен установленный dbt-core И adapter (`dbt-duckdb` для DuckDB).\n\n**Правильная установка для CI:**\n\n```bash\npip install \\\n dbt-core==1.10.0 \\\n dbt-duckdb==1.10.0 \\\n sqlfluff==3.4.0 \\\n sqlfluff-templater-dbt==3.4.0\n```\n\nВажно — **версии sqlfluff и sqlfluff-templater-dbt должны совпадать**. Если 3.4.0 и 3.3.0 -> ImportError на внутренних API.\n\n**В pre-commit конфиге то же самое через `additional_dependencies`:**\n\n```yaml\n- id: sqlfluff-lint\n additional_dependencies:\n - dbt-core==1.10.0\n - dbt-duckdb==1.10.0\n - sqlfluff-templater-dbt==3.4.0\n```\n\n**Дополнительная гочча**: в CI нужен валидный `profiles.yml`. Если `~/.dbt/profiles.yml` не существует, dbt templater упадёт с 'Could not find profile'. Решение — либо положить файл в CI step, либо использовать env var `DBT_PROFILES_DIR=./.dbt` и положить `profiles.yml` в репо.\n\n**Хорошая практика** — отдельный `profiles-ci.yml` в репо специально для CI/lint runs (DuckDB target, не требует credentials).

Проверьте понимание

Результат: 0 из 0
Прикладной
Вопрос 1 из 6. В .sqlfluff команда хочет 'включить все правила кроме нескольких раздражающих'. Какой подход правильный?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 4