Skip to main content

Tools with batteries included to extract CTE from a large SQL query and split them iteratively into dbt models.

Project description

cte2dbt 🚀

PyPI - Downloads PyPI - Status GitHub License GitHub Issues

Overview

This small Python module transforms large SQL queries with multiple Common Table Expressions (CTE) into modular, reusable dbt models.

If you have ever struggled with maintaining long, monolithic SQL queries filled with CTE, this tool is for you! cte2dbt automates the extraction of CTE and converts them into structured dbt models, preserving their dependencies and making them easier to test, document, and reuse.

Why use cte2dbt?

  • Automates SQL-to-dbt migration – No more manually splitting queries into models.
  • Preserves dependencies – Ensures each dbt model is built in the correct order.
  • Enables testing & modularisation – Improves maintainability and performance.
  • Customisable transformation functions – Tailor how CTE and source tables are processed.
  • Iterable design – Process, validate, or visualise models however you like.

Example Usage

Transforming a SQL query into dbt models

1️⃣ Import cte2dbt and sqlglot

import com.github.piotr_yuxuan.cte2dbt as cte2dbt
from sqlglot import parse_one

2️⃣ Define a SQL query

sql_query = """
WITH
  customers_cte AS (
    SELECT id, name FROM customers
  ),
  orders_cte AS (
    SELECT c.id, o.amount
    FROM customers_cte AS c
    JOIN prod.retails.orders AS o ON c.id = o.customer_id
  )
SELECT * FROM orders_cte;
"""

3️⃣ Define dbt transformation functions

to_dbt_ref_block = lambda name: f"{{{{ ref('{name}') }}}}"
to_dbt_source_block = lambda table: f"{{{{ source('{table.db}', '{table.name}') }}}}"

4️⃣ Initialise the provider

provider = cte2dbt.Provider(
    model_name="final_model",
    expr=parse_one(sql_query),
    to_dbt_ref_block=to_dbt_ref_block,
    to_dbt_source_block=to_dbt_source_block
)

5️⃣ Iterate over dbt models in execution order

The order guarantees that current model only relies on models that came earlier in the iteration.

for model_name, model_expr in provider.iter_dbt_models():
    print(f"-- Model: {model_name}")
    print(model_expr.sql(pretty=True))

Output:

-- Model: cte1
SELECT
  id,
  name
FROM {{ source('my_source', 'customers') }} AS customers

-- Model: cte2
SELECT
  cte1.id,
  orders.amount
FROM {{ ref('cte1') }} AS cte1
JOIN {{ source('my_source', 'orders') }} AS orders
  ON cte1.id = orders.customer_id

-- Model: final_model_name
SELECT
  *
FROM {{ ref('cte2') }} AS cte2

6️⃣ Generate the model dependency graph

print(provider.model_dependencies())
# Output:
# {'customers_cte': {'customers'},
#  'orders_cte': {'customers_cte', 'prod.retails.orders'},
#  'final_model': {'orders_cte'}}

Installation

Install cte2dbt using Poetry:

poetry add cte2dbt

Use Cases

Beyond just transforming SQL queries into dbt models, cte2dbt provides an iterable interface that unlocks multiple possibilities:

🔹 Run SQL transformations dynamically

Iterate over models and execute each as a temporary table:

for model_name, model_expr in provider.iter_dbt_models():
    conn.execute(f"CREATE TEMPORARY TABLE {model_name} AS {model_expr.sql()}")

🔹 Inspect intermediate data structures

Use DESCRIBE TABLE or DESCRIBE RESULT LAST_QUERY_ID()to log or visualise schema changes:

for model_name, _ in provider.iter_dbt_models():
    print(conn.execute(f"DESCRIBE TABLE {model_name}").fetchall())

🔹 Validate data consistency

Compare model output with existing tables to measure similarity:

for model_name, model_expr in provider.iter_dbt_models():
    similarity_score = compare_with_reference(conn, model_expr.sql(), reference_table)
    print(f"{model_name}: similarity {similarity_score}%")

🔹 Generate dependency graphs

Understand how changes in one CTE ripple through your final model:

import networkx as nx
import matplotlib.pyplot as plt

graph = nx.DiGraph(provider.model_dependencies())
nx.draw(graph, with_labels=True)
plt.show()

🔹 Export models to files

Save each model as a .sql file with a structured naming strategy:

for model_name, model_expr in provider.iter_dbt_models():
    with open(f"models/{model_name}.sql", "w") as f:
        f.write(model_expr.sql(pretty=True))

Extract column lists and generate corresponding .yml files:

for model_name, model_expr in provider.iter_dbt_models():
    columns = extract_columns(model_expr)
    write_yaml(f"models/{model_name}.yml", {"columns": columns})

How It Works

📌 SQL Parsing & CTE Extraction

cte2dbt leverages sqlglot to parse SQL queries at the token level. It then identifies CTE, source tables, and their dependencies.

📌 Modular dbt Model Generation

  • CTE are transformed into separate dbt models;
  • Source tables are converted into source() blocks;
  • CTE references are replaced with ref() calls;
  • Execution order is preserved to guarantee correctness.

📌 Fully Flexible API

Rather than enforcing a rigid output format, cte2dbt gives you full control over how models are transformed, stored, or executed.

Technical Details

  • Uses sqlglot for robust SQL parsing;
  • Implements dependency resolution to determine the correct order of model execution;
  • Supports fully customisable transformation functions;
  • Can be used programmatically or integrated into dbt workflows.

🚀 Conclusion

cte2dbt makes SQL-to-dbt migrations effortless, ensuring cleaner, modular, and testable SQL transformations. Whether you want to refactor legacy queries, improve maintainability, or generate structured dbt models automatically, this tool provides an elegant and flexible solution.

Give it a try and simplify your dbt workflow today! 🎯

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

cte2dbt-1.0.2.tar.gz (10.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

cte2dbt-1.0.2-py3-none-any.whl (11.4 kB view details)

Uploaded Python 3

File details

Details for the file cte2dbt-1.0.2.tar.gz.

File metadata

  • Download URL: cte2dbt-1.0.2.tar.gz
  • Upload date:
  • Size: 10.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/2.1.1 CPython/3.13.2 Linux/6.13.2-arch1-1

File hashes

Hashes for cte2dbt-1.0.2.tar.gz
Algorithm Hash digest
SHA256 91b5ecd6848fdb434f5ec159dab719b29d24c84c9ad936c5816d6f1cce283b4d
MD5 ca35bea595a5a748b8d9cb9f702084a7
BLAKE2b-256 2f0805dcfeadbd0e67c43b5be0d2b0e503a6a7646166783de9d1ebeab2961556

See more details on using hashes here.

File details

Details for the file cte2dbt-1.0.2-py3-none-any.whl.

File metadata

  • Download URL: cte2dbt-1.0.2-py3-none-any.whl
  • Upload date:
  • Size: 11.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/2.1.1 CPython/3.13.2 Linux/6.13.2-arch1-1

File hashes

Hashes for cte2dbt-1.0.2-py3-none-any.whl
Algorithm Hash digest
SHA256 c236600e957833ef11191592c05301e01ae790382c8f44690679ba0af1a17a80
MD5 609dab060a2e39f973f259475ffc5953
BLAKE2b-256 25d24514bc181a086f0b3c9b9f4cfe61274a88868ade4f21fff595eeca464b44

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page