SQL reference
CREATE TRANSFORMATION
Grammar, body rules, joins, folds, group targets, redefinition and removal of transformations.
For what a transformation is and how it behaves over time, read
Transformations first. This page is the statement reference.
The examples use the balances, transfers and token_prices tables.
Grammar
CREATE [OR REPLACE] TRANSFORMATION <name>
INTO <existing table> [ATTRIBUTE GROUP <group>]
AS <body>
<body> := a SELECT over one or more relations, with filters, GROUP BY folds,
windows and joins (INNER, LEFT, RIGHT, FULL, ASOF LEFT, LOOKUP)
DROP TRANSFORMATION [IF EXISTS] <name>
SHOW CREATE TRANSFORMATION <name>Body rules
- The body is a
SELECT; it may filter, compute, group, window and join. WHEREfilters and computed outputs (such asamount * 2 AS doubled) apply per row.GROUP BYfolds takeCOUNT,SUM,MIN,MAX,AVGandCOUNT(DISTINCT …), and an output may combine aggregate calls arithmetically.
Target rules
- The target is a table that exists before the statement runs.
- Output columns bind to target columns by name. Rename with
AS. - The transformation owns what it writes: the whole target, or one attribute group of it.
- With
ATTRIBUTE GROUP g, the transformation writes only groupg. Its output includes the target's full primary key, and every other output is a column ofg. - Each attribute group has one writer, so several transformations and producers maintain one row together.
A fold into an attribute group
CREATE TRANSFORMATION balance_activity INTO balances ATTRIBUTE GROUP activity AS
SELECT
chain_id,
recipient AS address,
token,
COUNT(*) AS transfer_count,
MAX(transferred_at) AS last_transfer_at,
MAX(_index) AS activity_index
FROM transfers
GROUP BY chain_id, recipient, token;The reply to a successful CREATE TRANSFORMATION carries "ddl_kind":4. From then on,
every transfer updates the activity group of the recipient's balance row, while another
producer writes the holding group.
A filtered transformation
CREATE OR REPLACE TRANSFORMATION large_transfer_activity
INTO balances ATTRIBUTE GROUP activity AS
SELECT
chain_id,
recipient AS address,
token,
COUNT(*) AS transfer_count,
MAX(transferred_at) AS last_transfer_at,
MAX(_index) AS activity_index
FROM transfers
WHERE amount > 1000000000
GROUP BY chain_id, recipient, token;Joins
A transformation body may join relations. The Joins page covers each kind.
| Join | In a transformation |
|---|---|
INNER JOIN, LEFT JOIN, RIGHT JOIN, FULL JOIN | Equality conditions between the sides. A change on any side re-derives the joined rows. |
ASOF LEFT JOIN | Each row joins the right row in force at its time. |
LOOKUP JOIN | Each event reads the lookup row by its key when it is processed and emits one row. |
CREATE TRANSFORMATION transfer_value_lookup INTO transfer_values AS
SELECT
t.chain_id AS chain_id,
t.token AS token,
t._index AS _index,
t.amount AS amount,
p.price_usd AS price_usd
FROM transfers t
LOOKUP JOIN token_prices p ON p.chain_id = t.chain_id AND p.token = t.token;Reading and writing the same table
A transformation may read a table and write one attribute group of it. The engine never
delivers the transformation's own writes back to it, and checks at CREATE that a chain of
transformations closes no loop through the groups it reads and writes.
Redefining: CREATE OR REPLACE
CREATE OR REPLACE TRANSFORMATION with an existing name installs a new revision of the
definition. The new revision reseeds the target from its sources, and
system.transformation_revisions and system.derived_lineage show the new revision.
DROP TRANSFORMATION
DROP TRANSFORMATION balance_activity;The target table keeps its rows. IF EXISTS makes a missing name a no-op:
DROP TRANSFORMATION IF EXISTS balance_activity;SHOW CREATE TRANSFORMATION
SHOW CREATE TRANSFORMATION balance_activity;