Learning Platform
Глоссарий Troubleshooting
Урок 14.02 · 20 мин
Начальный
Seedscolumn_typesquote_columnsschemaConfiguration

Конфигурация seeds

В предыдущем уроке dbt seed работал «магически»: положил CSV -> получил таблицу. На практике каждый второй CSV требует тюнинга. dbt угадывает типы по содержимому, и иногда угадывает неправильно — почтовый индекс 01234 превращается в integer 1234, ведущий ноль теряется. Дата 2026-05-19 остаётся VARCHAR’ом. Колонка is_active со значениями true/false становится строкой, а не boolean.

Чтобы это исправить, у seeds есть набор конфигов, которые задаются через dbt_project.yml или индивидуальный YAML-файл рядом с CSV.


Где конфигурируется seed

Два места:

  1. В dbt_project.yml под секцией seeds: — для дефолтов и групповых настроек.
  2. В YAML-файле рядом с CSV (например, seeds/_seeds.yml) — для индивидуальных настроек, descriptions, тестов.

Иерархия:

Иерархия конфигурации seeds

Самый специфичный конфиг побеждает.


column_types — точное указание типов

Это самая частая причина проблем с seed. dbt по умолчанию проходится по CSV и для каждой колонки выбирает «самый подходящий» тип. Логика проста:

  • Все значения — целые числа -> INTEGER (BIGINT в DuckDB).
  • Все значения — числа с точкой -> DOUBLE.
  • Иначе -> VARCHAR / TEXT.

Что идёт не так:

СлучайЧто хочетсяЧто получится по умолчанию
zip_code со значениями 01234, 02145VARCHAR (сохранить ведущие нули)INTEGER (ноль потерян: 1234)
date со значениями 2026-05-19DATEVARCHAR
is_active со значениями true, falseBOOLEANVARCHAR
amount_usd со значениями 100.50NUMERIC(10,2)DOUBLE (округление!)

Исправление — задать column_types явно в YAML рядом с CSV. Файл seeds/_seeds.yml:

version: 2

seeds:
  - name: country_codes
    description: "Маппинг ISO кодов стран на регионы"
    config:
      column_types:
        country_code: varchar(2)
        country_name: varchar(100)
        region: varchar(20)
        gdp_per_capita_usd: numeric(12,2)

После этого dbt seed создаст таблицу с этими типами, а не угаданными.

TIP

Если CSV содержит DATE, TIMESTAMP, BOOLEAN или NUMERIC с фиксированной точностью — всегда указывайте column_types. Особенно для финансовых данных: DOUBLE плохо хранит деньги (округление 0.1 + 0.2 = 0.30000000000000004).


Конкретный пример: финансовые данные

Файл seeds/exchange_rates.csv:

date,from_currency,to_currency,rate
2026-05-01,USD,EUR,0.92
2026-05-01,USD,GBP,0.79
2026-05-01,USD,RUB,87.50

Если ничего не настроить — dbt создаст:

КолонкаТип после dbt seed
dateVARCHAR — а нужно DATE
from_currencyVARCHAR — ок
to_currencyVARCHAR — ок
rateDOUBLE — а для финансов нужно NUMERIC(10,4)

Правильный конфиг:

seeds:
  - name: exchange_rates
    config:
      column_types:
        date: date
        from_currency: varchar(3)
        to_currency: varchar(3)
        rate: numeric(10,4)
    columns:
      - name: date
        description: "Дата курса"
        data_tests:
          - not_null
      - name: from_currency
        data_tests:
          - not_null
      - name: rate
        data_tests:
          - not_null

Обратите внимание: в YAML можно сразу повесить тесты на колонки seed, точно так же как на модель.


quote_columns — экранирование имён колонок

Если CSV содержит имя колонки с пробелом, дефисом, или зарезервированное слово (from, select, order), dbt по умолчанию не оборачивает их в кавычки. SQL-запросы упадут.

Пример: CSV region_data.csv:

country,from,order
US,2020,1
GB,2021,2

Здесь from и order — зарезервированные слова. dbt сгенерит:

CREATE TABLE region_data (country VARCHAR, from VARCHAR, order VARCHAR);
-- Syntax error: "from" — reserved keyword

Решение:

seeds:
  - name: region_data
    config:
      quote_columns: true

С quote_columns: true dbt оборачивает все имена в кавычки:

CREATE TABLE region_data ("country" VARCHAR, "from" VARCHAR, "order" VARCHAR);
WARNING

Если включить quote_columns: true, то во всех downstream-моделях нужно тоже оборачивать имена в кавычки: SELECT "from" FROM .... Лучше переименовать колонки в CSV (например, from -> start_year), чем тащить кавычки через весь проект.


+schema и +database — куда складывать seed

По умолчанию seed создаётся в той же схеме, что и модели проекта (target schema из profiles.yml). Можно переопределить:

# dbt_project.yml
seeds:
  jaffle_shop:                 # имя проекта из dbt_project.yml
    +schema: reference         # все seeds -> schema reference
    finance:                   # подпапка seeds/finance/
      +schema: finance_data    # seeds/finance/*.csv -> schema finance_data

В DuckDB схема — это просто namespace. Таблица создастся как reference.country_codes. В ref('country_codes') ничего не меняется — dbt сам подставит правильный schema.

Префикс + в +schema означает «это конфиг, а не имя поддиректории» — это синтаксис dbt для отделения групповых конфигов от иерархии папок.

Структура seeds с разными схемами

delimiter — нестандартный разделитель

Иногда CSV приходит с разделителем ; (Excel в Европе) или \t (TSV):

seeds:
  - name: european_data
    config:
      delimiter: ";"

или прямо в dbt_project.yml:

seeds:
  jaffle_shop:
    european_data:
      +delimiter: ";"

Дефолт — запятая. Если в CSV есть запятые внутри значений (например, "Tashkent, Uzbekistan"), они должны быть в двойных кавычках — это стандарт RFC 4180, dbt его поддерживает.


Полный пример: seeds/_seeds.yml

Хорошая практика — держать один YAML-файл рядом с CSV-файлами, описывающий все seeds в директории:

version: 2

seeds:
  - name: country_codes
    description: "ISO-3166 alpha-2 codes с маппингом на регионы и валюты"
    config:
      column_types:
        country_code: varchar(2)
        country_name: varchar(100)
        region: varchar(20)
        currency_code: varchar(3)
    columns:
      - name: country_code
        description: "ISO-3166 alpha-2 code"
        data_tests:
          - unique
          - not_null
      - name: country_name
        data_tests:
          - not_null
      - name: region
        data_tests:
          - accepted_values:
              values: ['Americas', 'EMEA', 'APAC']

  - name: exchange_rates
    description: "Дневные курсы USD к основным валютам"
    config:
      column_types:
        date: date
        from_currency: varchar(3)
        to_currency: varchar(3)
        rate: numeric(10,4)
    columns:
      - name: rate
        description: "Курс с точностью до 4 знаков"
        data_tests:
          - not_null
          - dbt_utils.expression_is_true:
              expression: "> 0"

Эти тесты запустятся при dbt test или dbt build — то есть seed валидируется на каждом ране CI.


Тесты на seed — это не магия

В dbt seed — это полноценная нода в DAG. На неё навешиваются:

  • data_tests (generic: not_null, unique, accepted_values, relationships) через YAML.
  • Singular tests (.sql файлы в tests/) могут ссылаться на seed через ref('country_codes').
  • dbt_utils тесты (equal_rowcount, expression_is_true) если установлен пакет.

Запуск:

dbt test --select country_codes  # только тесты на этом seed
dbt build --select +my_model     # включит seed + его тесты в цепочке

Что НЕ конфигурируется в seeds

В отличие от моделей, у seeds нет:

  • materialized — seed всегда table через DROP + CREATE + INSERT. Никаких view, incremental, ephemeral.
  • partition_by / cluster_by — нет (warehouse-specific конфиги моделей).
  • on_schema_change — нерелевантно, потому что каждый раз создаётся таблица заново.
  • pre_hook / post_hook — теоретически работают, но в 99% случаев не нужны.
  • incremental_strategy — seed это не incremental.

Если хочется управлять materialization — это не seed. Это inline-таблица в SQL-модели:

-- models/staging/_inline_lookup.sql
{{ config(materialized='table') }}

SELECT 'US' AS country, 'Americas' AS region UNION ALL
SELECT 'GB', 'EMEA' UNION ALL
SELECT 'JP', 'APAC'

Это антипаттерн для длинных списков (читаемость 0), но иногда уместно для 5-10 строк.


Попробуй сам

Создайте seeds/exchange_rates.csv:

date,from_currency,to_currency,rate
2026-05-01,USD,EUR,0.9205
2026-05-01,USD,GBP,0.7943
2026-05-02,USD,EUR,0.9220
2026-05-02,USD,GBP,0.7951

Запустите dbt seed без конфига. Посмотрите типы:

dbt show --inline "DESCRIBE exchange_rates"

Колонка date будет VARCHAR. Колонка rate — DOUBLE.

Теперь добавьте seeds/_seeds.yml с правильными column_types (date -> date, rate -> numeric(10,4)). Запустите dbt seed --full-refresh. Снова посмотрите типы.

Бонус: добавьте тест not_null на rate через YAML. Запустите dbt test --select exchange_rates. Что произойдёт, если поменять одну строку CSV на 2026-05-03,USD,EUR, (пустое значение в rate)?


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

  1. column_types — самый важный конфиг. Без него dbt угадывает типы и ошибается на датах, decimal, boolean, ZIP-кодах с ведущими нулями.
  2. quote_columns: true включается, если в CSV есть зарезервированные слова или специальные символы в именах колонок.
  3. +schema и +database управляют, в какую схему/базу попадёт seed. Префикс + отделяет конфиги от имён поддиректорий.
  4. delimiter — для CSV с ;, \t или другим разделителем.
  5. Конфигурация задаётся либо в dbt_project.yml (для дефолтов и групп), либо в YAML рядом с CSV (для индивидуальных настроек). YAML побеждает.
  6. На seed навешиваются тесты (not_null, unique, accepted_values) и descriptions точно так же, как на модель.
  7. Seed всегда materializes как table через DROP + CREATE + INSERT. Никаких view/incremental/ephemeral.
Constraints: типы данных и ограничения
Проверка знанийKnowledge check
Команда добавила seed `customers_vip.csv` с колонкой `customer_id` (значения вида '00042', '00193'). После `dbt seed` запрос `SELECT customer_id FROM customers_vip` возвращает '42', '193' — ведущие нули пропали. Как исправить?
ОтветAnswer
dbt угадал тип `customer_id` как INTEGER, потому что все значения — целые числа. INTEGER не хранит ведущие нули (42 == 00042). Исправление: явно задать `column_types` в YAML рядом с CSV: \n\n```yaml\nseeds:\n - name: customers_vip\n config:\n column_types:\n customer_id: varchar(10)\n```\n\nПосле этого `dbt seed --full-refresh` пересоздаст таблицу с правильным типом. Урок: для идентификаторов (особенно с padding нулями), почтовых индексов, телефонных номеров — **всегда явно VARCHAR**, никогда не доверяйте угадыванию.
Проверка знанийKnowledge check
В seed CSV есть колонка `created_at` с timestamp значениями `2026-05-19 14:30:00`. Команда хочет, чтобы dbt создал её как TIMESTAMP, и хочет проверить, что все значения not_null. Какой конфиг сделать?
ОтветAnswer
Два момента в YAML:\n\n```yaml\nversion: 2\nseeds:\n - name: events_log\n config:\n column_types:\n created_at: timestamp\n columns:\n - name: created_at\n data_tests:\n - not_null\n```\n\nКлюч `config.column_types.created_at: timestamp` задаёт тип при CREATE TABLE. Ключ `columns.data_tests` навешивает тест на колонку (запустится при `dbt test` или `dbt build`). После этого: `dbt seed --full-refresh` (важен флаг — без него dbt может попытаться INSERT в существующую таблицу со старым типом и упасть на конверсии). Затем `dbt test --select events_log` проверит not_null.

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

Результат: 0 из 0
Прикладной
Вопрос 1 из 5. Команда добавила seed `exchange_rates.csv` с колонкой `rate` (значения вида 0.9205, 0.7943, 1.234567). После `dbt seed` SUM(rate) даёт 2.85770000000003 вместо ожидаемых 2.8577. Что не так?

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

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

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

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