Skip to main content

create-sql-database-pipeline

Create a dlt pipeline from a SQL database source (postgres, mysql, mssql, oracle, sqlite, or any SQLAlchemy-supported database). Use when the user wants to load tables from a relational database to a destination like DuckDB, BigQuery, or Snowflake. Not for REST APIs or file sources.

소스 정보

저장소
dlt-hub/dlthub-ai-harness
최근 소스 활동
2026년 6월 3일 20:47
감지된 SKILL.md 언어
영어
스타
60
포크
5

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
create-sql-database-pipeline
description
Create a dlt pipeline from a SQL database source (postgres, mysql, mssql, oracle, sqlite, or any SQLAlchemy-supported database). Use when the user wants to load tables from a relational database to a destination like DuckDB, BigQuery, or Snowflake. Not for REST APIs or file sources.
argument-hint
[database-url-or-description] [destination]
# Create a SQL database dlt pipeline Build the simplest working pipeline — one table, no incremental loading — to get data flowing fast. **Docs:** https://dlthub.com/docs/dlt-ecosystem/verified-sources/sql_database Parse `$ARGUMENTS`: - `database` (required): description of the source database (e.g. "postgres on localhost", "Rfam MySQL", a connection URL, or just the DB type) - `destination` (optional, default `duckdb`): where to load data ## Steps ### 1. Assess data volume Ask the user: **how much data will be loaded?** (approximate row count or table size is enough) Note the answer — it will be used in step 10 to recommend the right backend and `chunk_size`. Key rules regardless of scale: - **Always pass `table_names=`** to `sql_database()` — avoids reflecting the entire schema - **Large tables need incremental loading** — full reload of tens of millions of rows on every run is almost never the right plan; flag this to the user before writing code ### 2. Snapshot current folder Run `ls -la` to see the current state before scaffolding. ### 3. Run dlthub pipeline init ``` uv run dlthub --non-interactive pipeline init sql_database <destination> ``` Note: `--non-interactive` is a global flag on `dlthub` and may appear at any position in the command. Always pass it to prevent prompts that block execution. If the command fails with `invalid choice: 'pipeline'`, the dlthub workspace is not initialized. Run `uv run dlthub init` and follow its instructions — most importantly run `uv sync` to pull required dependencies — then retry. This creates: - `sql_database_pipeline.py` — working example - `.dlt/secrets.toml` — credentials template - `.dlt/config.toml` — pipeline config - `requirements.txt`, `.gitignore` Run `ls -la` again to confirm what was created. ### 4. Research before writing code Do these in parallel: **Read essential dlt docs upfront:** - SQL database source overview: `https://dlthub.com/docs/dlt-ecosystem/verified-sources/sql_database` (setup steps: `https://dlthub.com/docs/dlt-ecosystem/verified-sources/sql_database/setup.md`) - Backend options and performance: `https://dlthub.com/docs/dlt-ecosystem/verified-sources/sql_database/configuration.md` - Credentials setup: `https://dlthub.com/docs/general-usage/credentials/setup.md` **Identify the driver:** Different databases need different SQLAlchemy dialect + driver packages: | Database | drivername | extra package | |---|---|---| | PostgreSQL | `postgresql+psycopg2` | `psycopg2-binary` | | MySQL / MariaDB | `mysql+pymysql` | `pymysql` | | MS SQL Server | `mssql+pyodbc` | `pyodbc` | | Oracle | `oracle+cx_oracle` | `cx_Oracle` | | SQLite | `sqlite` | _(built-in)_ | Install the driver + sql_database extras if missing: ``` uv add "dlt[hub,sql_database]" <driver-package> ``` ### 5. Read generated files Read the following (do NOT read `secrets.toml`): - `sql_database_pipeline.py` — read for scaffold patterns, then replace its contents with the real pipeline in step 6 - `.dlt/config.toml` — pipeline config structure ### 6. Write the pipeline **Replace the scaffold** — write the real pipeline into the generated `sql_database_pipeline.py`, or rename the file to match the use case (e.g. `movies_pipeline.py`). Either way: do not create a second file alongside the scaffold. The scaffold has no further purpose once replaced. **Choose one scenario based on how many tables to load:** #### Scenario A — Single table: use `sql_table` ```python import dlt from dlt.sources.sql_database import sql_table def main() -> None: pipeline = dlt.pipeline( pipeline_name="<name>", destination="<destination>", dataset_name="<name>", dev_mode=True, # fresh timestamped dataset on each run progress="log", # NOTE: progress goes on dlt.pipeline(), NOT on pipeline.run() ) table = sql_table( table="<table_name>", chunk_size=500, # schema="<schema>", # set if not the default schema ) load_info = pipeline.run(table.add_limit(1), write_disposition="replace") print(load_info) if __name__ == "__main__": main() ``` #### Scenario B — Multiple tables: use `sql_database` ```python import dlt from dlt.sources.sql_database import sql_database def main() -> None: pipeline = dlt.pipeline( pipeline_name="<name>", destination="<destination>", dataset_name="<name>", dev_mode=True, progress="log", ) source = sql_database( schema="<schema>", # set if not the default schema table_names=["<t1>", "<t2>"], # always pass table_names — avoids full-schema reflection chunk_size=500, ) load_info = pipeline.run(source.add_limit(1), write_disposition="replace") print(load_info) if __name__ == "__main__": main() ``` Key rules: - `progress="log"` belongs on `dlt.pipeline()`, **not** on `pipeline.run()` — that parameter does not exist on `run()` - `dev_mode=True` creates a new timestamped dataset on every run — keeps test runs non-destructive - `.add_limit(1)` loads one chunk only — always use for first/test runs - **Always pass `table_names=`** to `sql_database()` — omitting it reflects the entire schema, which is slow and loads unwanted tables - Do not hardcode credentials in the script — they are auto-injected from `.dlt/secrets.toml` ### 7. Apply reflection level Using the reflection level chosen in `find-source`, set it on the source: ```python # Scenario A table = sql_table(table="<table_name>", chunk_size=500, reflection_level="full") # Scenario B source = sql_database(table_names=[...], chunk_size=500, reflection_level="full") ``` - `minimal` — column names and nullability only; types inferred from data. Use when `full` causes casting errors. - `full` — column names, nullability, and data types including decimal precision/scale. Default; works for most cases. - `full_with_precision` — maximum detail including precision for text/binary. Use when the destination requires strict typing; may cause type-mismatch errors. Ref: https://dlthub.com/docs/dlt-ecosystem/verified-sources/sql_database/advanced#column-reflection ### 8. Add transformation callbacks (if needed) If the user needs to transform data before or during loading, introduce the right tool depending on when the transformation happens: - **Filter rows at SQL level** — `query_adapter_callback`: ```python def query_adapter(query, table): if table.name == "orders": return query.where(table.c.status == "active") return query source = sql_database(query_adapter_callback=query_adapter) ``` - **Add or modify columns at schema level** — `table_adapter_callback` to append computed columns before extraction. - **Transform rows after extraction** — `add_map` on a resource: ```python def pseudonymize(row): row["email"] = hash(row["email"]) return row source.orders.add_map(pseudonymize) ``` Ref: https://dlthub.com/docs/dlt-ecosystem/verified-sources/sql_database/usage If no transformation is needed, skip this step. > **Boundary:** these callbacks are for *extraction-time* transforms — filtering, masking, or reshaping data before it hits the destination. For *post-load* modeling (Kimball dimensions, CDM, cross-source joins) hand off to the **transformations** toolkit instead. ### 9. Set up config and secrets **Config** (non-secret values like schema name, table name): Edit `.dlt/config.toml` directly: ```toml # .dlt/config.toml [sources.sql_database] table_name = "<table>" ``` **Secrets** (credentials): **never** read or write `secrets.toml` directly. Present this template to the user and ask them to fill it in. Use `secrets_update_fragment` MCP tool (or `dlthub ai secrets` CLI) to write the fragment — do not edit the file directly: ```toml # .dlt/secrets.toml [sources.sql_database.credentials] drivername = "<dialect+driver>" # e.g. mysql+pymysql, postgresql+psycopg2 host = "<host>" port = <port> username = "<username>" password = "<password>" database = "<database>" ``` A connection string is also accepted: ```toml [sources.sql_database.credentials] credentials = "<dialect+driver>://user:password@host:port/database" ``` **ALWAYS Get Feedback** before running for the first time. Show a summary of the files you changed or created, and confirm the user has filled in credentials. ### 10. Debug pipeline — first run When user is ready, run: ``` uv run python <pipeline_script>.py ``` Expected output shows extract/normalize/load steps with row counts and timing from `progress="log"`. **When errors occur, use `debug-pipeline`** to diagnose — do not add more complexity first. Common first-run errors: - `ConfigFieldMissingException` — a credentials field is missing or misnamed in secrets.toml - `OperationalError` / `Can't connect` — wrong host/port/credentials or DB unreachable - `ModuleNotFoundError: No module named 'sqlalchemy'` — run `uv add "dlt[hub,sql_database]"` - `MissingDependencyException: numpy required` — the pyarrow backend also needs numpy: `uv add numpy` ### 11. Suggest backend after a successful test run Using the data volume noted in step 1, recommend the right backend: | Scale | Rows / Size | Recommended backend | chunk_size | | --- | --- | --- | --- | | Small | < 100k rows / < 500 MB | `sqlalchemy` (default) | any | | Medium | 100k – 10M rows / 500 MB – 10 GB | `pyarrow` (needs `numpy`) | 5000 | | Large | > 10M rows / > 10 GB | `connectorx` (MySQL/Postgres) or `pyarrow` | 50000 | Backend options: - **`sqlalchemy`** — safest, works with every destination, but slowest. - **`pyarrow`** — 20–30x faster; preserves decimal/date types precisely. Also requires `numpy`. - **`pandas`** — convenient for DataFrame workflows, but loses precision on decimal and date columns. - **`connectorx`** — fastest overall (2x over pyarrow, Rust-based); uses its own connection string format, bypasses SQLAlchemy. Ref: https://dlthub.com/docs/dlt-ecosystem/verified-sources/sql_database/configuration#configuring-the-backend Apply the chosen backend: ```python table = sql_table(table="<table_name>", chunk_size=500, backend="pyarrow") ``` Re-run the test to confirm the backend works before moving on. > **Note:** When using `pyarrow`, `pandas`, or `connectorx` (normalization skipped), `apply_hints` works for incremental loading, write disposition, merge keys, etc. — but schema changes like `columns={...}` do not work. Use `table_adapter_callback` for column-level schema changes instead. ## Next steps - **Test run succeeded, backend chosen** → use `adjust-table` to remove limits and add incremental loading for production - **Pipeline errors or 0 rows** → use `debug-pipeline` to inspect traces and load packages - **Want more tables** → use `add-table` to add resources to the pipeline - **Ready to explore data** → hand over to **data-exploration** toolkit - **Ready to deploy** → hand over to **dlthub-platform** toolkit
GitHub에서 보기