Skip to content
Keboola Docs

Transform Data

Create an SQL transformation that joins the octopus sightings with their species names, depth zones and ocean basins, and learn how mappings keep Storage safe.

Of the 10,000 sightings, 4,465 carry a depth — and the deepest of them comes from 4,838 meters down. The sightings table already says that much; what it will not say is which octopus that was or where in any human sense, because it names species by a numeric ID and locations as raw coordinates. This step joins occurrences, species, depth_zones and basins with SQL into one 10,000-row atlas that turns IDs into names and places every sighting in its ocean — and, where a depth was recorded, its depth zone — and it introduces the mapping model that keeps your source data safe while you do it. Step 3 of the Getting Started arc.

Two ways to do it — pick a tab in Set it up below. Doing it with Kai, the run is part of the prompt — you only check the result. Doing it yourself, you run it afterwards in the section below the tabs.

Four tables in Storage — occurrences, species, depth_zones and basins — from Get Your Data In. They sit in whatever bucket the connector created, and its name contains a configuration ID, so yours will not match the screenshots.

That does not matter. What the SQL depends on is the Table name you give each table in the input mapping below: those must be exactly occurrences, species, depth_zones and basins, or you have to edit the queries to match.

A transformation never runs against your Storage tables directly. Keboola copies the tables you ask for into a temporary database schema, runs your queries there, and copies back only the results you ask for. Three settings control that:

  1. Input mapping — which Storage tables get copied in, and what they are called inside the transformation. Anything you do not list is not visible to your code.
  2. Output mapping — which tables your code produces get written back to Storage, and where. Anything you do not list is thrown away when the job ends.
  3. Queries — the SQL itself, organized into named code blocks.

That is the safeguard: the only tables your transformation can change are the ones named in the output mapping. It is also what lets Keboola track data lineage across the project.

Screenshot - How mapping works

Writing SQL transformations is Kai’s home ground (SQL Transformations), and it already has your table schemas in front of it. Open Kai Agent in the top bar and ask:

Create a SQL transformation called "Octopus atlas" writing to out.c-octopus-atlas.octopus_atlas.
Start from every column of occurrences with latitude, longitude and depth_m cast to numbers.
Join species on aphia_id, adding scientific_name, family, and a display_name that falls back to
the scientific name when common_name is empty. Add depth_zones.zone_name as depth_zone by depth
range (left join — many sightings have no depth). Add basins.basin by latitude and longitude
range; use half-open ranges (>= min and < max) so boundary points match exactly one basin.
Outline the approach first, then build it and run it.

Spelling out the derived columns is the point — ask only for “a map table” and you will get a different set, and the check below will not match. The half-open-ranges sentence matters most: with BETWEEN, a sighting sitting exactly on a basin boundary lands in two basins and the row count comes out above 10,000. Asking for the outline first is worth it too: you get to read the joins before they run. One difference from the hand-built version: Kai builds the transformation on read-only input, so you will not see an input mapping in its configuration — the mapping sections on this page describe the hand-built path.

Check: 10,000 rows, 12 columns — the original 7 plus the five named in Run it and check the result, below the tabs. If the count differs, the joins differ: re-prompt, or fix the code in place. Later steps only depend on the table name, so a close-enough table is fine.

Coming from the Kai tab? The run already happened — the prompt asked for it. Skip to the row-count check below.

Click Run Transformation and confirm with Run. That creates a background job which copies the input tables in, runs your SQL, and writes octopus_atlas back to Storage. The job log spells the mechanism out — “Loading 4 tables to workspace”, then a line per table cloned into the workspace — which is the mapping model from the top of this page, in action.

Screenshot - Running the transformation

A notification appears with a Show job link — Snowflake SQL job has been scheduled, or your backend’s equivalent — and you can also find it under Jobs. It takes about a minute, and a green Success means it worked.

Screenshot - Successful job

Then open Storage: there is a new bucket out.c-octopus-atlas — listed as octopus-atlas with an OUT badge, the same way the in.c- prefix was hidden in step 2 — holding octopus_atlas — 10,000 rows and 12 columns. That row count is the same as the source occurrences table, which is the quickest sanity check that the joins matched every row without duplicating any: more than 10,000 means a range join double-matched, fewer means an inner join dropped sightings.

The 12 columns are the original 7 plus the five the SQL added: scientific_name, family, display_name, depth_zone and basin. Open the Data Sample tab and the atlas already answers things the raw file could not — sort by depth_m descending and the deepest row is Grimpoteuthis challengeri — a dumbo octopus, though the data only knows its Latin name — recorded at 4,838 meters in the abyssal zone.

The table list also has a Recently Updated By column, naming the transformation and its backend (Octopus atlas / Snowflake SQL here) — the fastest way to answer “where did this table come from?” months later.

Screenshot - The new table in Storage

Running the transformation again simply rebuilds the table; it is safe to re-run while you are experimenting.

  • Object 'SPECIES' does not exist (Snowflake). A table is missing from the input mapping, or the Table name inside the transformation differs from what the SQL uses. Snowflake uppercases unquoted identifiers, which is why every identifier is double-quoted. On BigQuery the equivalent error is Table ... was not found.
  • The job succeeds but Storage has no new table. The output mapping is empty or names a table your SQL never creates. The names must match exactly: octopus_atlas.
  • Numeric value '' is not recognized. Tables loaded from CSV arrive as text columns unless you give them types, so an empty cell is '' rather than NULL — and depth_m is empty on more than half of these rows, which is why the queries cast with TRY_TO_DOUBLE (Snowflake) / SAFE_CAST (BigQuery) before comparing. If you see this error, a cast got dropped somewhere.
  • The atlas has more than 10,000 rows. A range join double-matched: a sighting sitting exactly on a boundary fell into two basins or two zones. That happens when BETWEEN (inclusive on both ends) replaces the half-open >= min AND < max the queries use.
  • Invalid columns: _timestamp when the output is written. Your input tables were staged by cloning, which copies Keboola’s internal _timestamp column along with the data — and that column cannot be written back to Storage. It is not backend-specific and it is not a problem with the query. The clean fix is the dropTimestampColumn option on the input mapping; see the _timestamp system column. The run behind these screenshots was copy-staged, so the column never appeared.
  • You want to see what the query actually returns before saving. That is what a workspace is for.
  • You would rather not read the log yourself. Kai has it open already (Troubleshooting): My transformation "Octopus atlas" failed. Read the last job's log and tell me what to fix.
  • Use a Workspace — develop and test queries against a copy of the data before committing them to a transformation. This is how the work is really done.

Next: Send your data somewhere →

Ask Kai

Hi, I'm Kai — Keboola's AI assistant for the docs. Ask me anything and I'll answer from the documentation and cite the pages I use.

Kai is an AI and can make mistakes. Check the sources it links.