Three systems.
One query.
Join orders in Snowflake, sessions in BigQuery and a product catalog in Parquet with one ChalkSQL query. Each source gets the filters and columns it can handle, sends back only what the query needs, and Chalk joins the rest.
Ask across systems
Fernwood Supply, a fictional outdoor-gear retailer, keeps orders in Snowflake, web sessions in BigQuery and its product catalog as Parquet on GCS. Pick a question to see the query, what each source was asked to do, how many rows it sent back and the answer. What you see first is a recorded run; Run live sends the same query to the same three systems again.
Recorded run · 1,503 ms end to end
SELECT p.category,
COUNT(*) AS orders,
SUM(CAST(o.QUANTITY AS DOUBLE) * o.UNIT_PRICE_USD) AS revenue_usd,
SUM(CAST(o.QUANTITY AS DOUBLE) * (o.UNIT_PRICE_USD - p.unit_cost_usd)) AS margin_usd
FROM andymoreland_snowflake.SYNTHETIC_EXAMPLES.FERNWOOD_ORDERS AS o
JOIN read_parquet('gs://chalk-staging-test-data/demos/cross-source-sql/products.parquet') AS p
ON o.SKU = p.sku
WHERE o.ORDER_TS >= TIMESTAMPTZ '2026-09-10 00:00:00+00'
AND o.REGION = 'mountain'
GROUP BY p.category
ORDER BY revenue_usd DESC- Filter
- ORDER_TS >= TIMESTAMP '2026-09-10 00:00:00' AND REGION = 'mountain'
- Columns
- SKU, QUANTITY, UNIT_PRICE_USD, ORDER_TS, REGION
- Filter
- none pushed: every row is read
- Columns
- sku, category, unit_cost_usd
- 1Inner hash join on o.SKU = p.sku
- 2Aggregate by p.category
- 3Sort
| category | orders | revenue_usd | margin_usd |
|---|---|---|---|
| Tents | 50 | $16,955 | $9,917 |
| Apparel | 72 | $14,203 | $10,182 |
| Footwear | 42 | $9,687 | $6,917 |
| Cookware | 84 | $8,576 | $6,055 |
| Climbing | 39 | $7,485 | $4,905 |
| Sleep | 15 | $5,208 | $3,596 |
| Packs | 11 | $1,697 | $1,209 |
How a federated query runs
Moving whole tables to one place before you can join them is slow and expensive. A federated query moves the question instead, and only the rows that answer it travel.
Every source is a table
Snowflake, BigQuery, Postgres and the rest of your integrations appear as <integration>.<schema>.<table>, and files on S3 or GCS are one read_parquet() away. Nothing is copied first.
Each source does its share
The planner splits the query by source and hands each warehouse the filters and columns it can evaluate, as SQL in its own dialect. Parquet scans read only the columns the query uses.
Chalk does the rest
The rows that come back meet in Chalk's vectorized engine: joins across systems, time-window conditions, aggregates and sorts, in one plan.
Query the integrations you already have
Any data source connected to your Chalk environment is already a ChalkSQL table. Run a query from the SQL editor in the dashboard, from Python with run_sql, or from the chalk CLI, and put EXPLAIN in front of it to see what each source will be asked to do.
More on federated queries-- Marketing sessions in BigQuery, orders in Snowflake, the catalog as Parquet on GCS.
SELECT p.product_name, p.category,
COUNT(*) AS orders,
SUM(CAST(o.QUANTITY AS DOUBLE) * o.UNIT_PRICE_USD) AS revenue_usd
FROM (
SELECT customer_id, session_start
FROM marketing_bq.analytics.web_sessions
WHERE campaign = 'crag-season'
) AS s
JOIN finance_snowflake.SALES.ORDERS AS o
ON o.CUSTOMER_ID = s.customer_id
JOIN read_parquet('gs://catalog-exports/products.parquet') AS p
ON p.sku = o.SKU
WHERE o.ORDER_TS >= s.session_start
AND o.ORDER_TS < s.session_start + INTERVAL '2 hours'
GROUP BY p.product_name, p.category
ORDER BY revenue_usd DESC
LIMIT 5Writing queries that push down
The less each source sends back, the faster the join. A few habits keep the work in the warehouse.
Filters on a source's own columns
=, <>, <, <=, >, >=, IS NULL and IS NOT NULL against a literal, combined with AND and OR, become a WHERE clause in the source's SQL. Spell short value lists out with OR.
Filter in a subquery when joins compare ranges
When a join also has a time window, write each source's filters in a subquery over that source. They are pushed into the source's SQL instead of applied after the join.
Select what you use
Column lists are pushed to every source, Parquet included. A query that names six columns moves six columns over the network, however wide the table is.
No copies to keep in sync
Query data where it lives. There is no pipeline to build before a question can span two systems, and no stale replica to reconcile afterwards.
Each source's permissions apply
Every source is read through its own integration and credentials, so a query can only see what those integrations are allowed to see.
Measured, not guessed
Each run records the SQL every source received, how many rows it returned and how long it took, so you can see exactly where the time went.
Ask the question, not the pipeline
Bring the warehouses and lakes you already run. Talk to an engineer about querying them together with ChalkSQL.