> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sdf.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Migrating from DBT

> SDF is best used as an entire transformation layer in itself, effectively replacing DBT and adding all the benefits of SDF.

## Overview

Migrating from DBT to SDF is a straightforward process. The migration process involves:

1. **Converting your DBT models to SDF models** - This is a simple process of copying your SQL files into the SDF workspace and removing any `ref`, `source` or `config` jinja macro references.
2. **Mimicking DBT Configuration in SDF** - Take any specific YML configurations from your DBT project and add them in `.sdf.yml` files. See more details in [our YML reference](/reference/sdf-yml).

<Tip>
  SDF's open source DBT migration tool will be available soon to automate this process.
</Tip>

This guide will walk through migrating DBT's `jaffle_shop` example to SDF. It will also migrate away from the DuckDB usage in DBT and use SDF's built-in database, [SDF DB](/database/introduction), instead.

<Note>
  The result of this guide, i.e. a fully migrated DBT / DuckDB jaffle\_shop is available as an SDF sample. Use `sdf new --sample jaffle_shop` to create it locally, or
  check out the public source code for the workspace [here](https://github.com/sdf-labs/sdf-cli/tree/main/examples/jaffle_shop).
</Note>

## Prerequisites

The DBT DuckDB `jaffle_shop` example project should be setup locally. You can clone it from [here](https://github.com/dbt-labs/jaffle_shop_duckdb?tab=readme-ov-file#what-is-this-repo).

## Guide

<Steps>
  <Step title="Analyze the DBT Project">
    Upon first inspection of the `dbt_project.yml`, you'll notice a few things:

    1. All models are materialized to the `jaffle_shop` database
    2. By default, models are materialized as tables, but all models in the staging folde are materialized as views.
    3. The `jaffle_shop` project uses data locally via seeds. These are therefore required to build and run the project.

    With this in mind, this guide will first migrate over the data seeds, then the models.

    The end result will be a successul `sdf compile` and `sdf run` all locally.
  </Step>

  <Step title="Create a New SDF Workspace">
    Let's start by creating a simple SDF workspace to provide us with the skaffolding for the migration. We provide a sample called `hello_from_dbt` which skaffolds some of the basic configuration to match a DBT project.

    To do so, run the following command:

    ```shell theme={null}
    sdf new jaffle_shop --sample hello_from_dbt && cd jaffle_shop/
    ```

    This will create a new SDF workspace with the name `jaffle_shop` in a subdirectory. The `cd` places us into this directory so that we're ready to start migrating.

    <Note>
      This sample sets `preprocessor` to `jinja` and adds a seeds directory to match a DBT project structure. This is represented by the following yml block:

      ```yaml theme={null}
        defaults:
          preprocessor: jinja
      ```
    </Note>
  </Step>

  <Step title="Migrate Over the Data">
    Next, we'll migrate over the seeds to make sure SDF can interpret the schemas from the data.

    To do so, copy the files in the `seeds` directory from the DBT project to the SDF workspace.

    The resulting workspace should look like this:

    <div className="bg-[#0F1117] dark:bg-codeblock rounded-xl dark:ring-1 dark:ring-gray-800/50 relative">
      <pre style={{ fontFamily: 'monospace', backgroundColor: 'transparent' }} className="language-shell">
        <code className="language-shell">
          tmp/jaffle\_shop/
          ├── models
          │   └── main.sql
          ├── seeds
          │   ├── raw\_customers.csv
          │   ├── raw\_orders.csv
          │   └── raw\_payments.csv
          └── workspace.sdf.yml

          3 directories, 5 files
        </code>
      </pre>
    </div>

    Next, let's tell SDF to materialize these seeds as tables for use in the workspace.

    Add the following file in the directory `models/raw` and call it `seeds.sdf.yml`.

    ```yaml models/raw/seeds.sdf.yml theme={null}
    table:
      name: raw_customers
      location: seeds/raw_customers.csv
      with-header: true
      file-format: csv
    ---
    table:
      name: raw_orders
      location: seeds/raw_orders.csv
      with-header: true
      file-format: csv
    ---
    table:
      name: raw_payments
      location: seeds/raw_payments.csv
      with-header: true
      file-format: csv
    ```

    <Note>
      Note that since we put this yml in the `raw` directory, we intend to materialize these tables into the `raw` schema. This is merely an organizational choice. Seed tables can be materialized to any database or schema, and will default to the default database and schema if only the table name is specified.
    </Note>

    <Info>
      `Location` specification for seeds is relative to the root of the SDF workspace.
    </Info>

    The resulting SDF YML should like like this:

    <div className="bg-[#0F1117] dark:bg-codeblock rounded-xl dark:ring-1 dark:ring-gray-800/50 relative">
      <pre style={{ fontFamily: 'monospace', backgroundColor: 'transparent' }} className="language-shell">
        <code className="language-shell">
          workspace:
            name: jaffle\_shop
            edition: "1.3"

            defaults:
              preprocessor: jinja
              materialization: table

            includes:
              - path: models 
                index: schema-table-name
              - path: seeds
                type: resource
        </code>
      </pre>
    </div>

    Lastly, let's run a compile to make sure SDF can successfully process the data.

    ```shell theme={null}
    sdf compile --show all
    ```

    <div className="bg-[#0F1117] dark:bg-codeblock rounded-xl dark:ring-1 dark:ring-gray-800/50 relative">
      <pre style={{ fontFamily: 'monospace', backgroundColor: 'transparent' }} className="language-shell">
        <code className="language-shell">
          Working set 3 .sdf files
             Finished 3 models \[3 succeeded] in 0.912 secs

          Schema jaffle\_shop.raw\.raw\_customers
          ┌─────────────┬───────────┬────────────┬─────────────┐
          │ column\_name ┆ data\_type ┆ classifier ┆ description │
          ╞═════════════╪═══════════╪════════════╪═════════════╡
          │ id          ┆ bigint    ┆            ┆             │
          │ first\_name  ┆ varchar   ┆            ┆             │
          │ last\_name   ┆ varchar   ┆            ┆             │
          └─────────────┴───────────┴────────────┴─────────────┘

          Schema jaffle\_shop.raw\.raw\_orders
          ┌─────────────┬───────────┬────────────┬─────────────┐
          │ column\_name ┆ data\_type ┆ classifier ┆ description │
          ╞═════════════╪═══════════╪════════════╪═════════════╡
          │ id          ┆ bigint    ┆            ┆             │
          │ user\_id     ┆ bigint    ┆            ┆             │
          │ order\_date  ┆ date      ┆            ┆             │
          │ status      ┆ varchar   ┆            ┆             │
          └─────────────┴───────────┴────────────┴─────────────┘

          Schema jaffle\_shop.raw\.raw\_payments
          ┌────────────────┬───────────┬────────────┬─────────────┐
          │ column\_name    ┆ data\_type ┆ classifier ┆ description │
          ╞════════════════╪═══════════╪════════════╪═════════════╡
          │ id             ┆ bigint    ┆            ┆             │
          │ order\_id       ┆ bigint    ┆            ┆             │
          │ payment\_method ┆ varchar   ┆            ┆             │
          │ amount         ┆ bigint    ┆            ┆             │
          └────────────────┴───────────┴────────────┴─────────────┘
        </code>
      </pre>
    </div>

    Great! Our workspace compiled, and SDF successfully interpreted the table schemas from the data.
  </Step>

  <Step title="Migrate the First Models">
    Now that we've migrated our seed data over, let's migrate a model that selects from these tables.

    Let's start with the staging layer. Copy the `stg_orders.sql`, `stg_payments.sql`, and `stg_customers.sql` files from the DBT project to the SDF workspace, and place them into a directory called `staging`, we'll touch on why later.

    Your workspace should now look like this:

    <div className="bg-[#0F1117] dark:bg-codeblock rounded-xl dark:ring-1 dark:ring-gray-800/50 relative">
      <pre style={{ fontFamily: 'monospace', backgroundColor: 'transparent' }} className="language-shell">
        <code className="language-shell">
          tmp/jaffle\_shop/
          ├── models
          │   ├── main.sql
          │   ├── raw
          │   │   └── seeds.sdf.yml
          │   └── staging
          │       ├── stg\_customers.sql
          │       ├── stg\_orders.sql
          │       └── stg\_payments.sql
          ├── seeds
          │   ├── raw\_customers.csv
          │   ├── raw\_orders.csv
          │   └── raw\_payments.csv
          └── workspace.sdf.yml

          5 directories, 9 files
        </code>
      </pre>
    </div>

    Currently, SDF is including all models in the model directory without an index. However, for organization purposes, it's best practice to have SDF interpret the schema name from the directory structure. This is why we placed the model into the `staging` directory. For more on this, see our [Indexing Guide](/guide/advanced/index).

    To enable this indexing, let's slightly modify the workspace YML includes path to have it interpret the schema from the directory name and the table from the file name.

    ```yaml workspace.sdf.yml theme={null}
    includes:
      - path: models 
        index: schema-table-name
    ```

    Lastly, since this model is using a DBT-native feature, `ref()`, we'll need to replace this with a simple table selection. **This is the only modification required!**

    <Tip>
      Since SDF understands SQL natively, we can simply select from the table directly by its name. SDF still supports systematic renaming, lineage, and dependency analysis without the requirement for DBT refs in Jinja.
    </Tip>

    Below are the before and after SQL files for the `stg_orders.sql`, `stg_payments.sql`, and `stg_customers.sql` models. Scroll right within the filename section to see all files and changes.

    <CodeGroup>
      ```sql models/staging/stg_orders.sql (Before Migration) theme={null}
        with source as (

            {#-
            Normally we would select from the table here, but we are using seeds to load
            our data in this project
            #}
            select * from {{ ref('raw_orders') }}

        ),

        renamed as (

            select
                id as order_id,
                user_id as customer_id,
                order_date,
                status

            from source

        )

        select * from renamed
      ```

      ```sql models/staging/stg_orders.sql (After Migration) theme={null}
        with source as (
            
            {#-
            Normally we would select from the table here, but we are using seeds to load
            our data in this project
            #}
            select * from raw.raw_orders

        ),

        renamed as (

            select
                id as order_id,
                user_id as customer_id,
                order_date,
                status

            from source

        )

        select * from renamed
      ```

      ```sql models/staging/stg_payments.sql (Before Migration) theme={null}
        with source as (

            {#-
            Normally we would select from the table here, but we are using seeds to load
            our data in this project
            #}
            select * from {{ ref('raw_payments') }}

        ),

        renamed as (

            select
                id as payment_id,
                order_id,
                payment_method,

                -- `amount` is currently stored in cents, so we convert it to dollars
                amount / 100 as amount

            from source

        )

        select * from renamed

      ```

      ```sql models/staging/stg_payments.sql (After Migration) theme={null}
        with source as (

            {#-
            Normally we would select from the table here, but we are using seeds to load
            our data in this project
            #}
            select * from raw.raw_payments

        ),

        renamed as (

            select
                id as payment_id,
                order_id,
                payment_method,

                -- `amount` is currently stored in cents, so we convert it to dollars
                amount / 100 as amount

            from source

        )

        select * from renamed

      ```

      ```sql models/staging/stg_customers.sql (Before Migration) theme={null}
        with source as (

            {#-
            Normally we would select from the table here, but we are using seeds to load
            our data in this project
            #}
            select * from {{ ref('raw_customers') }}

        ),

        renamed as (

            select
                id as customer_id,
                name,
                email

            from source

        )

        select * from renamed
      ```

      ```sql models/staging/stg_customers.sql (After Migration) theme={null}
        with source as (
            
            {#-
            Normally we would select from the table here, but we are using seeds to load
            our data in this project
            #}
            select * from raw.raw_customers

        ),

        renamed as (

            select
                id as customer_id,
                name,
                email

            from source

        )

        select * from renamed
      ```
    </CodeGroup>

    <Info>
      Why select `raw.raw_orders`? Since we set an index to `schema-table-name`, the default schema for models in the `staging` folder is `staging`. Therefore, we need to tell SDF to look in the `raw` schema for this table.
    </Info>

    Let's run a compile to make sure SDF can successfully process the model.

    ```shell theme={null}
    sdf compile
    ```

    <div className="bg-[#0F1117] dark:bg-codeblock rounded-xl dark:ring-1 dark:ring-gray-800/50 relative">
      <pre style={{ fontFamily: 'monospace', backgroundColor: 'transparent' }} className="language-shell">
        <code className="language-shell">
          Working set 3 model files, 3 .sdf files
            Compiling jaffle\_shop.staging.stg\_orders (./models/staging/stg\_orders.sql)
            Compiling jaffle\_shop.staging.stg\_payments (./models/staging/stg\_payments.sql)
            Compiling jaffle\_shop.staging.stg\_customers (./models/staging/stg\_customers.sql)
             Finished 6 models \[6 succeeded] in 0.958 secs
        </code>
      </pre>
    </div>

    Fantastic! We've just compiled two models selecting from our data.

    If we want to take this one step further, we can **run** these models entirely locally with SDF DB.

    Let's try running our staging models with the following command:

    ```shell theme={null}
    sdf run "staging.*"
    ```

    <div className="bg-[#0F1117] dark:bg-codeblock rounded-xl dark:ring-1 dark:ring-gray-800/50 relative">
      <pre style={{ fontFamily: 'monospace', backgroundColor: 'transparent' }} className="language-shell">
        <code className="language-shell">
          Working set 3 model files, 2 .sdf files
              Running jaffle\_shop.staging.stg\_customers (./models/staging/stg\_customers.sql)
              Running jaffle\_shop.staging.stg\_orders (./models/staging/stg\_orders.sql)
              Running jaffle\_shop.staging.stg\_payments (./models/staging/stg\_payments.sql)
             Finished 6 models \[6 succeeded] in 1.008 secs

          Table jaffle\_shop.staging.stg\_orders
          ┌──────────┬─────────────┬────────────┬───────────┐
          │ order\_id ┆ customer\_id ┆ order\_date ┆ status    │
          ╞══════════╪═════════════╪════════════╪═══════════╡
          │ 1        ┆ 1           ┆ 2018-01-01 ┆ returned  │
          │ 2        ┆ 3           ┆ 2018-01-02 ┆ completed │
          │ 3        ┆ 94          ┆ 2018-01-04 ┆ completed │
          │ 4        ┆ 50          ┆ 2018-01-05 ┆ completed │
          │ 5        ┆ 64          ┆ 2018-01-05 ┆ completed │
          │ 6        ┆ 54          ┆ 2018-01-07 ┆ completed │
          │ 7        ┆ 88          ┆ 2018-01-09 ┆ completed │
          │ 8        ┆ 2           ┆ 2018-01-11 ┆ returned  │
          │ 9        ┆ 53          ┆ 2018-01-12 ┆ completed │
          │ 10       ┆ 7           ┆ 2018-01-14 ┆ completed │
          └──────────┴─────────────┴────────────┴───────────┘
          99 rows, showing only 10 rows.
            Run with --limit 0 to show all rows.

          Table jaffle\_shop.staging.stg\_customers
          ┌─────────────┬────────────┬───────────┐
          │ customer\_id ┆ first\_name ┆ last\_name │
          ╞═════════════╪════════════╪═══════════╡
          │ 1           ┆ Michael    ┆ P.        │
          │ 2           ┆ Shawn      ┆ M.        │
          │ 3           ┆ Kathleen   ┆ P.        │
          │ 4           ┆ Jimmy      ┆ C.        │
          │ 5           ┆ Katherine  ┆ R.        │
          │ 6           ┆ Sarah      ┆ R.        │
          │ 7           ┆ Martin     ┆ M.        │
          │ 8           ┆ Frank      ┆ R.        │
          │ 9           ┆ Jennifer   ┆ F.        │
          │ 10          ┆ Henry      ┆ W.        │
          └─────────────┴────────────┴───────────┘
          100 rows, showing only 10 rows.
            Run with --limit 0 to show all rows.

          Table jaffle\_shop.staging.stg\_payments
          ┌────────────┬──────────┬────────────────┬────────┐
          │ payment\_id ┆ order\_id ┆ payment\_method ┆ amount │
          ╞════════════╪══════════╪════════════════╪════════╡
          │ 1          ┆ 1        ┆ credit\_card    ┆ 10     │
          │ 2          ┆ 2        ┆ credit\_card    ┆ 20     │
          │ 3          ┆ 3        ┆ coupon         ┆ 1      │
          │ 4          ┆ 4        ┆ coupon         ┆ 25     │
          │ 5          ┆ 5        ┆ bank\_transfer  ┆ 17     │
          │ 6          ┆ 6        ┆ credit\_card    ┆ 6      │
          │ 7          ┆ 7        ┆ credit\_card    ┆ 16     │
          │ 8          ┆ 8        ┆ credit\_card    ┆ 23     │
          │ 9          ┆ 9        ┆ gift\_card      ┆ 23     │
          │ 10         ┆ 9        ┆ bank\_transfer  ┆ 0      │
          └────────────┴──────────┴────────────────┴────────┘
          113 rows, showing only 10 rows.
            Run with --limit 0 to show all rows.
        </code>
      </pre>
    </div>

    Great! We've successfully migrated our first models and run them locally with SDF DB.
  </Step>

  <Step title="Migrate Complex Jinja">
    Moving on, let's try migrating a model that uses more complex Jinja. SDF supports Jinja natively via the open source [MiniJinja](https://docs.rs/minijinja/latest/minijinja/) processor.
    As such, even with complex jinja the only thing we need to change is the `ref()` macro.

    Let's migrate the `orders` model to demonstrate this. Let's imagine we want to materialize the layer that selects from `staging` into the `analytics` schema.

    Since we are using the `schema-table-name` index, we can move the `orders.sql` file from the DBT project into a directory called `orders` in the SDF workspace.

    Just like before, we'll need to modify the SQL to remove the `ref()` macro. See below for the before and after.

    <CodeGroup>
      ```sql models/analytics/orders.sql (After Migration) theme={null}
      {% set payment_methods = ['credit_card', 'coupon', 'bank_transfer', 'gift_card'] %}

      with orders as (

          select * from staging.stg_orders

      ),

      payments as (

          select * from staging.stg_payments

      ),

      order_payments as (

          select
              order_id,

              {% for payment_method in payment_methods -%}
              sum(case when payment_method = '{{ payment_method }}' then amount else 0 end) as {{ payment_method }}_amount,
              {% endfor -%}

              sum(amount) as total_amount

          from payments

          group by order_id

      ),

      final as (

          select
              orders.order_id,
              orders.customer_id,
              orders.order_date,
              orders.status,

              {% for payment_method in payment_methods -%}

              order_payments.{{ payment_method }}_amount,

              {% endfor -%}

              order_payments.total_amount as amount

          from orders


          left join order_payments
              on orders.order_id = order_payments.order_id

      )

      select * from final
      ```

      ```sql models/analytics/orders.sql (Before Migration) theme={null}
      {% set payment_methods = ['credit_card', 'coupon', 'bank_transfer', 'gift_card'] %}

      with orders as (

          select * from {{ ref('stg_orders') }}

      ),

      payments as (

          select * from {{ ref('stg_payments') }}

      ),

      order_payments as (

          select
              order_id,

              {% for payment_method in payment_methods -%}
              sum(case when payment_method = '{{ payment_method }}' then amount else 0 end) as {{ payment_method }}_amount,
              {% endfor -%}

              sum(amount) as total_amount

          from payments

          group by order_id

      ),

      final as (

          select
              orders.order_id,
              orders.customer_id,
              orders.order_date,
              orders.status,

              {% for payment_method in payment_methods -%}

              order_payments.{{ payment_method }}_amount,

              {% endfor -%}

              order_payments.total_amount as amount

          from orders


          left join order_payments
              on orders.order_id = order_payments.order_id

      )

      select * from final
      ```
    </CodeGroup>

    Let's run a compile to make sure SDF can successfully process the model.

    ```shell theme={null}
    sdf compile
    ```

    <div className="bg-[#0F1117] dark:bg-codeblock rounded-xl dark:ring-1 dark:ring-gray-800/50 relative">
      <pre style={{ fontFamily: 'monospace', backgroundColor: 'transparent' }} className="language-shell">
        <code className="language-shell">
          Working set 4 model files, 3 .sdf files
            Compiling jaffle\_shop.staging.stg\_payments (./models/staging/stg\_payments.sql)
            Compiling jaffle\_shop.staging.stg\_customers (./models/staging/stg\_customers.sql)
            Compiling jaffle\_shop.staging.stg\_orders (./models/staging/stg\_orders.sql)
            Compiling jaffle\_shop.analytics.orders (./models/analytics/orders.sql)
             Finished 7 models \[7 succeeded] in 1.191 secs
        </code>
      </pre>
    </div>

    And lastly, let's run our new model with the following command:

    ```shell theme={null}
    sdf run analytics.orders
    ```

    <div className="bg-[#0F1117] dark:bg-codeblock rounded-xl dark:ring-1 dark:ring-gray-800/50 relative">
      <pre style={{ fontFamily: 'monospace', backgroundColor: 'transparent' }} className="language-shell">
        <code className="language-shell">
          Working set 4 model files, 2 .sdf files
              Running jaffle\_shop.staging.stg\_orders (./models/staging/stg\_orders.sql)
              Running jaffle\_shop.staging.stg\_payments (./models/staging/stg\_payments.sql)
              Running jaffle\_shop.analytics.orders (./models/analytics/orders.sql)
             Finished 5 models \[5 succeeded] in 1.177 secs

          Table jaffle\_shop.analytics.orders
          ┌──────────┬─────────────┬────────────┬───────────┬────────────────────┬───────────────┬──────────────────────┬──────────────────┬────────┐
          │ order\_id ┆ customer\_id ┆ order\_date ┆ status    ┆ credit\_card\_amount ┆ coupon\_amount ┆ bank\_transfer\_amount ┆ gift\_card\_amount ┆ amount │
          ╞══════════╪═════════════╪════════════╪═══════════╪════════════════════╪═══════════════╪══════════════════════╪══════════════════╪════════╡
          │ 1        ┆ 1           ┆ 2018-01-01 ┆ returned  ┆ 10                 ┆ 0             ┆ 0                    ┆ 0                ┆ 10     │
          │ 2        ┆ 3           ┆ 2018-01-02 ┆ completed ┆ 20                 ┆ 0             ┆ 0                    ┆ 0                ┆ 20     │
          │ 4        ┆ 50          ┆ 2018-01-05 ┆ completed ┆ 0                  ┆ 25            ┆ 0                    ┆ 0                ┆ 25     │
          │ 6        ┆ 54          ┆ 2018-01-07 ┆ completed ┆ 6                  ┆ 0             ┆ 0                    ┆ 0                ┆ 6      │
          │ 8        ┆ 2           ┆ 2018-01-11 ┆ returned  ┆ 23                 ┆ 0             ┆ 0                    ┆ 0                ┆ 23     │
          │ 9        ┆ 53          ┆ 2018-01-12 ┆ completed ┆ 0                  ┆ 0             ┆ 0                    ┆ 23               ┆ 23     │
          │ 11       ┆ 99          ┆ 2018-01-14 ┆ completed ┆ 27                 ┆ 0             ┆ 0                    ┆ 0                ┆ 27     │
          │ 13       ┆ 84          ┆ 2018-01-17 ┆ completed ┆ 5                  ┆ 0             ┆ 14                   ┆ 0                ┆ 19     │
          │ 15       ┆ 25          ┆ 2018-01-17 ┆ completed ┆ 0                  ┆ 22            ┆ 0                    ┆ 0                ┆ 22     │
          │ 17       ┆ 71          ┆ 2018-01-18 ┆ completed ┆ 0                  ┆ 0             ┆ 2                    ┆ 0                ┆ 2      │
          └──────────┴─────────────┴────────────┴───────────┴────────────────────┴───────────────┴──────────────────────┴──────────────────┴────────┘
          99 rows, showing only 10 rows.
            Run with --limit 0 to show all rows.
        </code>
      </pre>
    </div>

    Nice! We just migrated a more complex model to compile and run entirely with SDF.
  </Step>

  <Step title="Configure Materialization">
    Next, let's ensure our new models match the materialization patterns from DBT. For more on configuring materialization, see our [Materialization Guide](/guide/setup/materialization).

    Firstly, let's set the default materialization for all models to `table`. To do so, add the following to your workspace YML:

    ```yaml workspace.sdf.yml theme={null}
    defaults:
      ...
      materialization: table
    ```

    Next, let's set the materialization for the staging models to `view`.
    To do so, we'll add one more includes block that overwrites the default materialization for any models in the `staging` directory:

    ```yaml workspace.sdf.yml theme={null}
    includes:
      ...
      - path: models/staging
        defaults:
          materialization: view
    ```

    Lastly, let's run a compile to make sure SDF can successfully process the model.

    ```shell theme={null}
    sdf compile
    ```

    <div className="bg-[#0F1117] dark:bg-codeblock rounded-xl dark:ring-1 dark:ring-gray-800/50 relative">
      <pre style={{ fontFamily: 'monospace', backgroundColor: 'transparent' }} className="language-shell">
        <code className="language-shell">
          Working set 4 model files, 4 .sdf files
            Compiling jaffle\_shop.staging.stg\_orders (./models/staging/stg\_orders.sql)
            Compiling jaffle\_shop.staging.stg\_payments (./models/staging/stg\_payments.sql)
            Compiling jaffle\_shop.staging.stg\_customers (./models/staging/stg\_customers.sql)
            Compiling jaffle\_shop.analytics.orders (./models/analytics/orders.sql)
             Finished 7 models \[7 succeeded] in 0.990 secs
        </code>
      </pre>
    </div>

    Sweet! Our models are now materialized as views in the staging layer and as tables everywhere else.
  </Step>

  <Step title="Migrate Tests">
    As a final step, let's migrate the tests from the DBT project to the SDF. For this example, we'll migrate all the tests that run on the staging layer in DBT's Jaffle Shop.

    Let's start by moving the `schema.yml` file within the `staging/` directory in the DBT project to the `staging/` directory in the SDF workspace. Next, let's rename it to `tests.sdf.yml`.

    The resulting workspace should look like this:

    <div className="bg-[#0F1117] dark:bg-codeblock rounded-xl dark:ring-1 dark:ring-gray-800/50 relative">
      <pre style={{ fontFamily: 'monospace', backgroundColor: 'transparent' }} className="language-shell">
        <code className="language-shell">
          tmp/jaffle\_shop/
          ├── models
          │   ├── analytics
          │   │   └── orders.sql
          │   ├── main.sql
          │   ├── raw
          │   │   └── seeds.sdf.yml
          │   └── staging
          │       ├── stg\_customers.sql
          │       ├── stg\_orders.sql
          │       ├── stg\_payments.sql
          │       └── tests.sdf.yml
          ├── seeds
          │   ├── raw\_customers.csv
          │   ├── raw\_orders.csv
          │   └── raw\_payments.csv
          └── workspace.sdf.yml

          6 directories, 11 files
        </code>
      </pre>
    </div>

    Next, let's migrate the DBT test YML to SDF test YML. The steps look like this:

    1. Remove the `version: 2` at the top of the file.
    2. Make each one of the listed models its own table block. Add `---` in between each block.
    3. Migrate the tests to the [SDF test](/guide/data-quality/tests) syntax.

    <Tip>
      Unlike DBT tests, SDF tests are functions that accept parameters and a severity property. This increases the flexibility of the tests and allows for more complex testing scenarios. For more on tests, see the [SDF Test Guide](/guide/data-quality/tests).
    </Tip>

    After going through these steps, the resulting `tests.sdf.yml` should look like this:

    <CodeGroup>
      ```yaml schema.yml (From DBT) theme={null}
      version: 2

      models:
        - name: stg_customers
          columns:
            - name: customer_id
              tests:
                - unique
                - not_null

        - name: stg_orders
          columns:
            - name: order_id
              tests:
                - unique
                - not_null
            - name: status
              tests:
                - accepted_values:
                    values: ['placed', 'shipped', 'completed', 'return_pending', 'returned']

        - name: stg_payments
          columns:
            - name: payment_id
              tests:
                - unique
                - not_null
            - name: payment_method
              tests:
                - accepted_values:
                    values: ['credit_card', 'coupon', 'bank_transfer', 'gift_card']
      ```

      ```yaml models/staging/tests.sdf.yml theme={null}
      table:
        name: stg_customers
        columns:
          - name: customer_id
            tests:
              - expect: unique()
              - expect: not_null()
      ---
      table:
        name: stg_orders
        columns:
          - name: order_id
            tests:
              - expect: unique()
              - expect: not_null()
          - name: status
            tests:
              - expect: in_accepted_values(['placed', 'shipped', 'completed', 'return_pending', 'returned'])
      ---
      table:
        name: stg_payments
        columns:
          - name: payment_id
            tests:
              - expect: unique()
              - expect: not_null()
          - name: payment_method
            tests:
              - expect: in_accepted_values(['credit_card', 'coupon', 'bank_transfer', 'gift_card'])
      ```
    </CodeGroup>

    To run the tests, simply run the following command:

    ```shell theme={null}
    sdf test
    ```

    <div className="bg-[#0F1117] dark:bg-codeblock rounded-xl dark:ring-1 dark:ring-gray-800/50 relative">
      <pre style={{ fontFamily: 'monospace', backgroundColor: 'transparent' }} className="language-shell">
        <code className="language-shell">
          Working set 4 model files, 3 test files, 7 .sdf files
              Running jaffle\_shop.staging.stg\_orders (./models/staging/stg\_orders.sql)
              Running jaffle\_shop.staging.stg\_payments (./models/staging/stg\_payments.sql)
              Running jaffle\_shop.staging.stg\_customers (./models/staging/stg\_customers.sql)
              Testing jaffle\_shop.staging.test\_stg\_orders (./sdftarget/dbg/tests/jaffle\_shop/staging/test\_stg\_orders.sql)
              Testing jaffle\_shop.staging.test\_stg\_payments (./sdftarget/dbg/tests/jaffle\_shop/staging/test\_stg\_payments.sql)
              Testing jaffle\_shop.staging.test\_stg\_customers (./sdftarget/dbg/tests/jaffle\_shop/staging/test\_stg\_customers.sql)
             Finished 6 models \[6 succeeded], 3 tests \[3 passed] in 1.050 secs
          \[Pass] Test jaffle\_shop.staging.test\_stg\_orders
          \[Pass] Test jaffle\_shop.staging.test\_stg\_payments
          \[Pass] Test jaffle\_shop.staging.test\_stg\_customers
        </code>
      </pre>
    </div>

    Woohoo! The tests ran locally again the data with the SDF DB, and they are passing.
  </Step>

  <Step title="(Optional) Migrate the Rest">
    If the steps above are followed for the rest of the models and tests, you'll see that the entire DBT project can be migrated to SDF with ease.

    The result is a workspace that looks like this:

    <div className="bg-[#0F1117] dark:bg-codeblock rounded-xl dark:ring-1 dark:ring-gray-800/50 relative">
      <pre style={{ fontFamily: 'monospace', backgroundColor: 'transparent' }} className="language-shell">
        <code className="language-shell">
          tmp/jaffle\_shop\_complete/
          ├── models
          │   ├── analytics
          │   │   ├── customers.sql
          │   │   ├── meta.sdf.yml
          │   │   └── orders.sql
          │   ├── raw
          │   │   └── seeds.sdf.yml
          │   └── staging
          │       ├── stg\_customers.sql
          │       ├── stg\_orders.sql
          │       ├── stg\_payments.sql
          │       └── tests.sdf.yml
          ├── seeds
          │   ├── raw\_customers.csv
          │   ├── raw\_orders.csv
          │   └── raw\_payments.csv
          └── workspace.sdf.yml

          6 directories, 12 files
        </code>
      </pre>
    </div>

    Then compiles:

    ```shell theme={null}
    sdf compile
    ```

    <div className="bg-[#0F1117] dark:bg-codeblock rounded-xl dark:ring-1 dark:ring-gray-800/50 relative">
      <pre style={{ fontFamily: 'monospace', backgroundColor: 'transparent' }} className="language-shell">
        <code className="language-shell">
          Working set 5 model files, 10 .sdf files
            Compiling jaffle\_shop.staging.stg\_payments (./models/staging/stg\_payments.sql)
            Compiling jaffle\_shop.staging.stg\_orders (./models/staging/stg\_orders.sql)
            Compiling jaffle\_shop.staging.stg\_customers (./models/staging/stg\_customers.sql)
            Compiling jaffle\_shop.analytics.orders (./models/analytics/orders.sql)
            Compiling jaffle\_shop.analytics.customers (./models/analytics/customers.sql)
             Finished 8 models \[8 succeeded] in 1.108 secs
        </code>
      </pre>
    </div>

    Runs:

    ```shell theme={null}
    sdf run
    ```

    <div className="bg-[#0F1117] dark:bg-codeblock rounded-xl dark:ring-1 dark:ring-gray-800/50 relative">
      <pre style={{ fontFamily: 'monospace', backgroundColor: 'transparent' }} className="language-shell">
        <code className="language-shell">
          Working set 5 model files, 10 .sdf files
              Running jaffle\_shop.staging.stg\_customers (./models/staging/stg\_customers.sql)
              Running jaffle\_shop.staging.stg\_orders (./models/staging/stg\_orders.sql)
              Running jaffle\_shop.staging.stg\_payments (./models/staging/stg\_payments.sql)
              Running jaffle\_shop.analytics.customers (./models/analytics/customers.sql)
              Running jaffle\_shop.analytics.orders (./models/analytics/orders.sql)
             Finished 8 models \[8 succeeded] in 1.236 secs
        </code>
      </pre>
    </div>

    and tests:

    ```shell theme={null}
    sdf test
    ```

    <div className="bg-[#0F1117] dark:bg-codeblock rounded-xl dark:ring-1 dark:ring-gray-800/50 relative">
      <pre style={{ fontFamily: 'monospace', backgroundColor: 'transparent' }} className="language-shell">
        <code className="language-shell">
          Working set 5 model files, 5 test files, 10 .sdf files
              Running jaffle\_shop.staging.stg\_orders (./models/staging/stg\_orders.sql)
              Running jaffle\_shop.staging.stg\_payments (./models/staging/stg\_payments.sql)
              Running jaffle\_shop.staging.stg\_customers (./models/staging/stg\_customers.sql)
              Testing jaffle\_shop.staging.test\_stg\_orders (./sdftarget/dbg/tests/jaffle\_shop/staging/test\_stg\_orders.sql)
              Testing jaffle\_shop.staging.test\_stg\_payments (./sdftarget/dbg/tests/jaffle\_shop/staging/test\_stg\_payments.sql)
              Testing jaffle\_shop.staging.test\_stg\_customers (./sdftarget/dbg/tests/jaffle\_shop/staging/test\_stg\_customers.sql)
              Running jaffle\_shop.analytics.customers (./models/analytics/customers.sql)
              Running jaffle\_shop.analytics.orders (./models/analytics/orders.sql)
              Testing jaffle\_shop.analytics.test\_orders (./sdftarget/dbg/tests/jaffle\_shop/analytics/test\_orders.sql)
              Testing jaffle\_shop.analytics.test\_customers (./sdftarget/dbg/tests/jaffle\_shop/analytics/test\_customers.sql)
             Finished 8 models \[8 succeeded], 5 tests \[5 passed] in 2.137 secs
          \[Pass] Test jaffle\_shop.staging.test\_stg\_payments
          \[Pass] Test jaffle\_shop.staging.test\_stg\_orders
          \[Pass] Test jaffle\_shop.staging.test\_stg\_customers
          \[Pass] Test jaffle\_shop.analytics.test\_customers
          \[Pass] Test jaffle\_shop.analytics.test\_orders
        </code>
      </pre>
    </div>

    all locally with SDF.
  </Step>
</Steps>

## Known Jinja Differences

Due to SDF using a slightly different Jinja processor, there are a few differences in how Jinja is processed. Below are some of the key differences:

1. **Iterating over Dictionaries** - Use `|items` instead of `.items()`

   ```jinja theme={null}
   {% for key, value in my_dict.items() %}
   ```

   becomes

   ```jinja theme={null}
   {% for key, value in my_dict|items() %}
   ```

   For more on this, see the section about iteration in our [Jinja Docs](/guide/macro-processing/intro-to-jinja#for-loops).

2. **Ternary `if` Statements with no `else`** - An `else` is required in ternary conditional statements.

   ```jinja theme={null}
   {{ 'yes' if boolean_var }}
   ```

   becomes

   ```jinja theme={null}
   {{ 'yes' if boolean_var else 'no' }}
   ```

   A real life example can be found in the [`dbt_utils.group_by` implementation](https://github.com/dbt-labs/dbt-utils/blob/main/macros/sql/groupby.sql)

   In DBT's implementation, the `else` is not required as seen here:

   ```jinja theme={null}
   group by {% for i in range(1, n + 1) -%}
     {{ i }}{{ ',' if not loop.last }}   
   {%- endfor -%}
   ```

   SDF's implementation requires an `else` after `loop.last`:

   ```jinja theme={null}
   group by {% for i in range(1, n + 1) -%}
     {{ i }}{{ ',' if not loop.last else "" }}   
   {%- endfor -%}
   ```

   This achieves the same result, but is more explicit in what happens in the negation case.

<Tip>
  If you find any others, please let us know via our [Slack Community](https://sdf.com/join) or [GitHub Issues](https://github.com/sdf-labs/sdf-cli/issues).
</Tip>
