Runs against your warehouse.
(Snowflake / BigQuery / ...)
rq transforms create mrr \ --schedule "0 2 * * *" \ --sql "SELECT ..."
Runs where the data lives.
No YAML, no Jinja, no adapter.
TL;DR
- Pick rawquery if you want transforms that run in the same product as ingestion, storage, and serving, with no Jinja, no YAML, no warehouse adapter config, and no per-seat pricing.
- Pick dbt if you have an analytics engineering team that lives in git, you rely on the dbt package ecosystem, you need MetricFlow for a semantic layer, or your warehouse is Snowflake / BigQuery / Databricks and you want the dbt standard.
Feature-by-feature
| Dimension | rawquery | dbt |
|---|---|---|
| Shape | Product (integrated) | Framework on top of a warehouse |
| Authoring | SQL + {{ ref() }} | Jinja SQL + YAML config |
| Required tools | CLI or web UI | Python, dbt CLI, git, adapter |
| Runtime | Integrated scheduler | dbt Cloud / Airflow / cron |
| Materializations | table, view, incremental | + ephemeral, snapshot, MV |
| Tests | Scheduled SQL checks | First-class assertion framework |
| Documentation | Lineage DAG in UI | Auto-generated docs site |
| Package ecosystem | None | dbt Hub |
| Semantic layer | None | MetricFlow |
| Pricing | Included in plan | ~$100 / seat / month (dbt Cloud) |
dbt is a framework. rawquery is a product.
dbt compiles SQL templates into DDL against a warehouse you already own. It assumes a lot: that you have a warehouse, adapter credentials, a git repo, a CI pipeline, and somebody who knows how to set up all of the above. In return, it gives you a rigorous model layer with tests, docs, and reusable packages. The analytics engineer's tool of choice.
rawquery's transforms are first-class objects in the same product as your data. They are not files in a repo and they do not require a second runtime. You write SQL, you give it a name, you optionally schedule it with a cron expression. References to other transforms use a single template: {{ ref('other_transform') }}. That is the full templating surface. If you want to commit the SQL to git, you can. The CLI still owns the transform object.
A transform, two ways
Same transform: monthly recurring revenue from Stripe subscriptions.
-- models/mrr.sql{{ config(materialized='incremental', unique_key='month') }}
SELECT date_trunc('month', created_at) AS month, sum(plan_amount) / 100.0 AS mrrFROM {{ source('stripe', 'subscriptions') }}WHERE status = 'active'{% if is_incremental() %} AND created_at > (SELECT max(month) FROM {{ this }}){% endif %}GROUP BY 1-- models/mrr.sql{{ config(materialized='incremental', unique_key='month') }}
SELECT date_trunc('month', created_at) AS month, sum(plan_amount) / 100.0 AS mrrFROM {{ source('stripe', 'subscriptions') }}WHERE status = 'active'{% if is_incremental() %} AND created_at > (SELECT max(month) FROM {{ this }}){% endif %}GROUP BY 1rq transforms create mrr \ --schedule "0 2 * * *" \ --sql "SELECT date_trunc('month', created_at) AS month, sum(plan_amount) / 100.0 AS mrr FROM my_stripe.subscriptions WHERE status = 'active' GROUP BY 1"rq transforms create mrr \ --schedule "0 2 * * *" \ --sql "SELECT date_trunc('month', created_at) AS month, sum(plan_amount) / 100.0 AS mrr FROM my_stripe.subscriptions WHERE status = 'active' GROUP BY 1"The dbt version is more powerful: the incremental block lets you backfill efficiently. The rawquery version is shorter and runs without installing dbt, configuring an adapter, or maintaining a repo. Both are valid. They target different people.
Where dbt still wins
- Ecosystem. dbt Hub packages (dbt_utils, dbt_expectations, audit_helper, vendor-shipped transformations from Fivetran / Stripe / Salesforce) are a serious productivity lever. We have no package registry.
- Testing. First-class assertions with clear semantics: unique, not_null, accepted_values, relationships, plus custom tests and generic tests. Ours are scheduled SQL checks. Less strict, more pragmatic.
- Documentation. Auto-generated dbt docs site with column-level lineage, source metadata, and exposure tracking. Our lineage view shows the DAG; their docs site shows the model.
- Semantic layer.MetricFlow gives you metric definitions, dimensional modeling, and a query API. We don't have one and it is not on the near-term roadmap.
- Portability. dbt Core is Apache 2.0 and runs against any supported warehouse. If you change warehouse, your models come with you. Our transforms run on rawquery. The Iceberg tables underneath are portable; the transform definitions are not.
- Snapshots. dbt snapshots implement slowly-changing-dimension type-2 semantics out of the box. We don't.
Try rawquery
# Install the CLIcurl -sSL rawquery.dev/install.sh | sh
# Connect your sourcerq connections create my-stripe --type stripe -p api_key=sk_live_xxxrq connections sync my-stripe
# Create a transform (SQL only, no YAML)rq transforms create active_customers \ --sql "SELECT id, email FROM my_stripe.customers WHERE status = 'active'"
# Chain transforms with {{ ref('...') }}rq transforms create active_count \ --schedule "0 * * * *" \ --sql "SELECT count(*) FROM {{ ref('active_customers') }}"# Install the CLIcurl -sSL rawquery.dev/install.sh | sh
# Connect your sourcerq connections create my-stripe --type stripe -p api_key=sk_live_xxxrq connections sync my-stripe
# Create a transform (SQL only, no YAML)rq transforms create active_customers \ --sql "SELECT id, email FROM my_stripe.customers WHERE status = 'active'"
# Chain transforms with {{ ref('...') }}rq transforms create active_count \ --schedule "0 * * * *" \ --sql "SELECT count(*) FROM {{ ref('active_customers') }}"