2.1 Query the lake with Trino¶
Concept¶
A distributed SQL engine lets you run ordinary SQL over data wherever it lives — files in object storage, or a live database — without moving it first. Trino is exactly that. You send it one SQL query; it figures out where the data is, does the work (splitting big scans across parallel workers, or pushing filters down into a source database), and returns one result set. To you it feels like a single database.
Trino doesn't store any data itself — it connects to other systems and queries them where
they already are. Each connection is a catalog: a named pointer to one data system, with
its own schemas and tables. SHOW CATALOGS lists them. On our stack:
| Catalog | Connects to… | Where the bytes physically live | What's inside | You use it |
|---|---|---|---|---|
shopflow |
the live ShopFlow app database (Postgres) | the Postgres server | the raw source tables — customers, products, orders, order_items |
now, Unit 2 |
iceberg |
the lakehouse (via the Apache Polaris Iceberg REST catalog) | Parquet files on RustFS object storage | the Bronze / Silver / Gold tables you'll build | from Unit 4 |
system |
Trino itself | in memory | engine info (nodes, running queries) | rarely |
Why is the lakehouse catalog called iceberg and not shopflow?
A catalog is named after the system it connects to, not the business:
shopflowis named after the Postgres database it points at (which is calledshopflow).icebergis named after the table format the lakehouse stores its tables in (Apache Iceberg — see 1.3).
So your ShopFlow data lives in two homes at different stages of its life: raw in the
shopflow catalog (Postgres, right now), and — after you build the pipeline in Unit 4 —
refined in the iceberg catalog (the lakehouse on RustFS). Same business, two places.
Do I ever create a catalog?
Not in SQL. A Trino catalog is a connection, set up once by the platform admin (a small
config file) — shopflow and iceberg already exist for you (CREATE CATALOG isn't a
Trino command; catalogs are configuration, not data). What you create lives inside
a catalog:
So in this unit you'll explore ShopFlow's raw source with SQL — the exact
tables from the schema page — before building the lakehouse. Every table
has a three-part name: catalog.schema.table (e.g. shopflow.public.orders). To see what's
inside any catalog, use SHOW SCHEMAS FROM <catalog> and SHOW TABLES FROM <catalog>.<schema>.
flowchart TB
U[You: one SQL query] --> T[Trino coordinator]
T --> PG[(shopflow catalog<br/>live Postgres — Unit 2)]
T --> LK[(iceberg catalog<br/>lakehouse on RustFS — Unit 4+)]
Same SQL, two homes
Everything you write this unit against shopflow.public.* will run unchanged on
your own lakehouse's demouser_lake.silver.* / demouser_lake.gold.* tables once you build them in Unit 4 — just
swap the three-part name. SQL is portable across sources; that's the whole point of a
query engine.
Lab¶
The stack is already running (see the architecture). You run the Trino SQL in this course in your browser, signed in with your lab account. Trino always knows who you are, so you see your own lakehouse and the shared data, and nobody else's.
A. SQLPad: your main SQL workbench (reads and writes). Open it at http://localhost:8003
(local) / sqlpad.de.lan (k8s), sign in with your lab account, and pick the connection
Lakehouse (Trino) — demouser (your own username). It reaches every Trino catalog you're allowed
to use: shopflow, adventureworks, the shared iceberg lake and your own demouser_lake. The
ShopFlow — OLTP and AdventureWorks — OLTP connections talk to Postgres directly, read-only
(it plays the shop's production database). You don't need them for these lessons, and course SQL
like TRY_CAST or USE fails there, because Postgres doesn't speak Trino's SQL.
The left sidebar shows only your own lake (<username>_lake). shopflow and adventureworks aren't
listed there, but you can still query them by full name, e.g. shopflow.public.orders. Run
SHOW CATALOGS; to see everything you can reach.
B. Superset SQL Lab: read-only, nice for charts. Open Superset at http://localhost:8004
(local) or superset.de.lan (k8s), go to SQL → SQL Lab, and pick the shopflow database
with the public schema. Skip the USE line, because in SQL Lab you choose the schema from the
dropdown instead. SQL Lab only runs SELECT, so use SQLPad for any lesson that creates or changes
data (CREATE/INSERT/UPDATE/MERGE/DDL).
What about the Trino CLI?
On the team stack, Trino only accepts queries from SQLPad and Superset, because they tell it
who you are. If you run the stack on your own laptop, you can also use the trino CLI
(trino --server http://localhost:8007; see Prerequisites).
SQLPad hides empty schemas
SQLPad's left schema sidebar only lists schemas that contain at least one table — so a
freshly created empty schema (e.g. sandbox right after CREATE SCHEMA) won't show up until
you create a table in it. After creating tables, click the refresh ↻ on the sidebar. You
can always query a schema by its full name (demouser_lake.sandbox.…) even if the sidebar hasn't
caught up — the sidebar is just for browsing.
The http://localhost:8007/ui/ page is monitoring, not a query editor
On the laptop stack, that web UI shows running and finished queries with their stats. It's handy for watching the engine work, but you write SQL in SQLPad or Superset, not there.
First — confirm it's working¶
Before anything else, run this smoke test. It checks two things: that the engine responds, and that it can reach the ShopFlow data.
-- 1) Is the engine alive?
SELECT 'Trino is working!' AS status;
-- 2) Can it read ShopFlow? (should return 40000)
SELECT count(*) AS orders FROM shopflow.public.orders;
The first query invents a one-row result out of thin air, so it proves only that Trino
received your SQL and ran it — the engine is alive. The second actually reaches into the
shopflow catalog and counts every row in the orders table (count(*) = "how many rows?");
getting 40000 back proves Trino can find and read the real data, not just echo text.
Expected result of the second query:
| orders |
|---|
| 40000 |
If the first query fails, your connection is wrong (check that you picked the Lakehouse (Trino)
connection in SQLPad, or that you're signed into Superset). If the first works but the second errors, the shopflow catalog or its
data isn't available — confirm the stack is fully up and the data has been seeded. Once both
succeed, you're ready.
Discover what Trino can see — the three-part name is built from these:
-- What data sources are connected?
SHOW CATALOGS; -- expect: shopflow, iceberg, system, your demouser_lake, …
-- What schemas live in the ShopFlow source?
SHOW SCHEMAS FROM shopflow; -- the business tables are in 'public'
-- What tables are there?
SHOW TABLES FROM shopflow.public; -- customers, products, orders, order_items
-- Peek at a table's columns
DESCRIBE shopflow.public.orders;
Read these top-down as a drill-in: SHOW CATALOGS lists every data source Trino is connected
to; SHOW SCHEMAS FROM <catalog> lists the schemas (groups of tables) inside one of them; and
SHOW TABLES FROM <catalog>.<schema> lists the tables inside one schema. Stack the answers and
you get the three-part name catalog.schema.table — e.g. shopflow + public + orders =
shopflow.public.orders, the full address of any one table.
Set a default catalog + schema so you can drop the prefix:
Run your first SELECTs against real ShopFlow data (~40,000 orders):
-- Projection + row limit (always LIMIT while exploring!)
SELECT order_id, customer_id, channel, status, order_ts
FROM orders
LIMIT 10;
-- Filter with WHERE, sort with ORDER BY
SELECT order_id, customer_id, order_ts
FROM orders
WHERE status = 'delivered' -- a completed sale
AND channel = 'web'
ORDER BY order_ts DESC
LIMIT 20;
ShopFlow order statuses
An order's status is one of placed · shipped · delivered · cancelled.
Throughout Unit 2 we treat delivered as a completed sale (revenue you can
count). Get used to filtering on it.
Once you've built the ShopFlow Gold tables (Unit 4), the exact same SQL works against the lakehouse — just change the three-part name:
-- From Unit 4 onward — your own lakehouse, demouser_lake
SHOW SCHEMAS FROM demouser_lake; -- expect: bronze, silver, gold
SELECT * FROM demouser_lake.gold.daily_sales ORDER BY order_date DESC LIMIT 14;
Make your own space in the lakehouse¶
So far you've only read data. Before we start writing (from 2.5 onward), use your
own area to experiment in. You can't create a catalog in SQL (that's admin config) — but you
already have one: demouser_lake (e.g. ravi_lake), created when you first signed in. Inside
it you can create your own schemas, and real tables inside those — your personal corner of
the lakehouse. (iceberg is the course's shared lake: read it, don't write to it.)
Signed in with your own account?
The examples use the default lab account demouser — its catalog is demouser_lake. If you
registered your own account, replace demouser with your username (SQLPad: find & replace,
Ctrl/Cmd + H), e.g. ravi_lake.
Create a schema and a first table:
CREATE SCHEMA IF NOT EXISTS demouser_lake.my_lab;
CREATE TABLE demouser_lake.my_lab.first_table (
id int,
item varchar,
amount double
);
INSERT INTO demouser_lake.my_lab.first_table VALUES
(1, 'keyboard', 49.9),
(2, 'mouse', 19.5);
SELECT * FROM demouser_lake.my_lab.first_table ORDER BY id;
Read it step by step:
CREATE SCHEMA … demouser_lake.my_lab— makes a new schema (a named folder for tables) inside your own lakehouse catalog.IF NOT EXISTSmeans "skip if it's already there" — safe to re-run.CREATE TABLE demouser_lake.my_lab.first_table (…)— defines a real Iceberg table: this writes table metadata to the catalog, and future rows land as Parquet files on RustFS (the object storage from Unit 1.2).INSERT INTO … VALUES …— your first write to the lakehouse. Each write appends a new Parquet file and a new table snapshot (the ACID table-format magic from 1.3).SELECT * FROM …— reads it straight back with the same three-part name.
You just built a datalake
That's the whole idea — a lakehouse is a catalog holding many schemas (yours: demouser_lake;
the course's shared one: iceberg). In Unit 4 you'll build a full bronze → silver → gold
exactly this way (with Spark instead of SQL). Clean up your experiment any time:
Why writing just works
demouser_lake is yours — you're its owner, so you can create and change anything in it
without asking. Anywhere else you need a grant: the course's shared lake is read-only for you,
and another member's lakehouse is closed until they share it. Ownership, grants and sharing
are Unit 9.
Challenge¶
Using only the orders table: list the 5 most recent cancelled orders placed on the
marketplace channel — show order_id, customer_id, and order_ts, newest first.
Solution
🎯 This runs unchanged on Azure, Databricks, Snowflake & Fabric
What you just did: connected to a distributed SQL engine and ran filtered, sorted
SELECTs over source tables using catalog.schema.table names.
- Azure Databricks — the same ANSI
SELECT / WHERE / ORDER BYruns on a SQL Warehouse; tables use the samecatalog.schema.tablenaming, addressed via the Databricks catalog. - Snowflake — same SQL in a Snowsight worksheet on a Virtual Warehouse; three-part
naming is
database.schema.table;SHOW SCHEMAS/SHOW TABLESwork the same. - Microsoft Fabric — query via the Lakehouse SQL analytics endpoint over OneLake
Delta; standard SQL,
TOPinstead ofLIMIT. - Azure Data Factory — no interactive query surface of its own; it moves data and points at a warehouse/engine to run the SQL.
Trino ≈ Databricks SQL Warehouse ≈ Snowflake Virtual Warehouse ≈ Fabric SQL endpoint. Your SQL is portable; only the connection changes.
Key terms, at a glance¶
| Term | Plain meaning |
|---|---|
| Distributed SQL engine | Runs one SQL query over data across sources, in parallel |
| Catalog | A named connection to a data source (its schemas + tables) |
| Schema | A named group of tables inside a catalog (ShopFlow's live in public) |
| Three-part name | catalog.schema.table — how you address any table |
SHOW CATALOGS / SCHEMAS / TABLES |
Drill in: list sources → schemas in one → tables in one |
| Federation | Querying a live source (like Postgres) in place, no copy |
| Predicate pushdown | Trino sends WHERE filters down into the source to read less |
You can now…¶
- Explain what a distributed SQL engine does, and connect to Trino via CLI or the UI
- Discover data with
SHOW CATALOGS/SCHEMAS/TABLESandDESCRIBE - Write filtered, sorted
SELECTs usingcatalog.schema.tablenames - Explain why the same SQL will run on the lakehouse catalog in Unit 4