Skip to content
Transformations

Snowflake to DuckDB Migration

This guide covers the key differences between Snowflake and DuckDB SQL dialects and provides practical advice for migrating your Snowflake transformations to DuckDB in Keboola.

SQLGlot can automatically convert approximately 85% of Snowflake SQL syntax to DuckDB. However, some constructs require manual adjustment. The sections below cover the most common differences you will encounter.

You can also use the Kai AI assistant in Keboola to help with migration.

Keboola provides an experimental Transformation Migration component (keboola.app-transformation-migration) that automates the migration of Snowflake transformations to DuckDB. The component handles SQL dialect translation, preserves input/output table mappings, and creates new DuckDB transformation configurations automatically.

Tutorial: Migrating Snowflake Transformations

Section titled “Tutorial: Migrating Snowflake Transformations”

In your Keboola project, go to Components and search for Transformation Migration. Create a new configuration. Set the following parameters:

  • Source Branch --- select the branch containing your Snowflake transformations (default: default).
  • Destination Branch --- choose an existing dev branch for the migrated configurations, or create a new one.
  • Source Transformation Type --- select Snowflake.
  • Destination Transformation Type --- select DuckDB.
  • Destination Config Name Pattern --- use %s as a placeholder for the original name (e.g., %s keeps the same name, %s_duckdb appends a suffix).

Migration Component Configuration

Click Load Transformations to populate the list of available Snowflake transformations. Select one or more transformations you want to migrate to DuckDB.

Select Transformations to Migrate

After selecting the transformations, click Save and then Run Component.

Save Configuration and Run

Once the job completes, review the migration summary in the job detail. The logs show which transformations were migrated successfully and provide links to the newly created DuckDB configurations.

Job Detail with Migration Results

Step 5: Review and Adjust the Migrated Configuration

Section titled “Step 5: Review and Adjust the Migrated Configuration”

Open the newly created DuckDB transformation. The component preserves all input/output table mappings and runtime settings. Review the SQL code carefully --- some queries may need manual adjustments due to syntax differences between Snowflake and DuckDB (see the sections below for common differences).

Migrated DuckDB Transformation

  • Uses SQLGlot to automatically translate SQL code blocks from Snowflake to DuckDB dialect.
  • Preserves all input/output table mappings and runtime settings (backend size, etc.).
  • If SQL translation fails for any code block, the original Snowflake SQL is preserved as comments in the DuckDB configuration, and an error is reported at the end of the migration.
  • The automatic SQL translation is not perfect. Complex queries, Snowflake-specific functions, and edge cases in data types may not be converted correctly.
  • Roughly 25% of migrated transformations will need some manual fixes --- typically related to case sensitivity, unsupported functions, or data type differences.
  • The component is experimental and may contain unhandled bugs or have various limitations.
  • Always test the migrated transformations on a dev branch before deploying to production.

This is one of the most critical differences when migrating.

  • Unquoted identifiers are converted to uppercase (MyTable becomes MYTABLE).
  • Quoted identifiers are case-sensitive ("MyTable" and "MYTABLE" are different tables).
  • Unquoted table names are converted to lowercase (MyTable becomes mytable).
  • Quoted table names are case-sensitive ("MyTable" references exactly MyTable).
  • Columns are always case-sensitive regardless of quoting (SELECT columnName and SELECT ColumnName refer to different columns).

Migration tip: Use consistent lowercase naming in DuckDB:

-- Recommended: use lowercase names
CREATE TABLE mytable AS SELECT ...;
SnowflakeDuckDBNotes
VARIANTJSON or VARCHARSemi-structured data
ARRAYLISTNative arrays
OBJECTSTRUCT or JSONNested objects
INTEGERINTEGERSame
VARCHARVARCHARSame
TIMESTAMPTIMESTAMPSame
FLOATFLOATSame
BOOLEANBOOLEANSame
DATEDATESame

Snowflake supports the QUALIFY clause for filtering window function results. DuckDB does not support QUALIFY; use a subquery with WHERE instead.

-- Snowflake
SELECT * FROM orders
QUALIFY ROW_NUMBER() OVER (PARTITION BY customer_id ORDER BY order_date DESC) = 1;
-- DuckDB
SELECT * FROM (
SELECT *, ROW_NUMBER() OVER (PARTITION BY customer_id ORDER BY order_date DESC) AS rn
FROM orders
) WHERE rn = 1;
-- Snowflake
SELECT NVL(column_a, 'default') FROM my_table;
-- DuckDB
SELECT COALESCE(column_a, 'default') FROM my_table;
-- Snowflake
SELECT IFF(status = 'active', 'yes', 'no') FROM users;
-- DuckDB
SELECT CASE WHEN status = 'active' THEN 'yes' ELSE 'no' END FROM users;
-- Snowflake
SELECT DATEADD(day, 7, order_date) FROM orders;
-- DuckDB
SELECT order_date + INTERVAL '7 days' FROM orders;
-- Snowflake
SELECT DATEDIFF(day, start_date, end_date) FROM projects;
-- DuckDB
SELECT date_diff('day', start_date, end_date) FROM projects;
-- Snowflake
SELECT department, LISTAGG(employee_name, ', ') WITHIN GROUP (ORDER BY employee_name)
FROM employees GROUP BY department;
-- DuckDB
SELECT department, STRING_AGG(employee_name, ', ')
FROM employees GROUP BY department;

The following functions have the same syntax in both Snowflake and DuckDB:

  • SUBSTRING(str, start, len)
  • POSITION('x' IN str)
  • CONCAT(a, b) --- both handle NULL gracefully
  • a || b --- both return NULL if any input is NULL
  • LIMIT n
  • FETCH FIRST n ROWS
  • COALESCE(a, b, c)
  • CAST(value AS type)

Same in both:

-- Basic table creation
CREATE TABLE customers (id INT, name VARCHAR);
-- Primary keys
CREATE TABLE customers (id INT PRIMARY KEY, name VARCHAR);
-- Temporary tables
CREATE TEMP TABLE temp_data (id INT);
CREATE TEMPORARY TABLE temp_data (id INT);
-- CREATE TABLE AS SELECT (CTAS)
CREATE TABLE new_table AS SELECT * FROM old_table;
-- CREATE OR REPLACE
CREATE OR REPLACE TABLE customers AS SELECT * FROM source;
-- IF NOT EXISTS
CREATE TABLE IF NOT EXISTS customers (id INT);

Key differences:

-- Snowflake only --- not supported in DuckDB
CREATE TRANSIENT TABLE staging_data (id INT, value VARCHAR);

DuckDB does not have a TRANSIENT keyword. Use regular or TEMP tables instead.

  • Snowflake: Temporary tables can be created in any schema.
  • DuckDB: Temporary tables are always in the temp.main schema (you cannot specify a different schema).
  • Both: Temporary tables are automatically dropped at the end of the session.

In DuckDB, a temporary table can have the same name as a regular table. The temporary table takes priority. To access the regular table explicitly, use its fully qualified name: memory.main.table_name.

CategorySnowflakeDuckDB
Window functionsQUALIFYROW_NUMBER() + WHERE
Null handlingNVL(a, b)COALESCE(a, b)
ConditionalsIFF(cond, a, b)CASE WHEN cond THEN a ELSE b END
Date mathDATEADD(unit, n, date)date + INTERVAL 'n unit'
Date diffDATEDIFF(unit, d1, d2)date_diff('unit', d1, d2)
String aggregationLISTAGG(...) WITHIN GROUPSTRING_AGG(...)
Temp tablesCREATE TEMPORARY TABLECREATE TEMP TABLE (same)
Transient tablesCREATE TRANSIENT TABLENot supported
  1. Start with SQLGlot --- it handles most syntax conversions automatically.
  2. Watch out for case sensitivity --- Snowflake defaults to uppercase, DuckDB to lowercase.
  3. Test queries incrementally --- migrate one block at a time and verify results.
  4. Use Kai AI assistant --- it can help identify and fix syntax differences.
  5. Check column aliases --- DuckDB handles column aliases differently than Snowflake in some contexts. GROUP BY ALL can help resolve alias-related issues.
  6. Verify aggregate functions --- if your input tables are non-typed, enable Infer input table data types or add explicit CAST() calls to avoid type errors with functions like SUM() or AVG().

For local development, DuckDB provides a Snowflake extension that lets you attach data from Snowflake and work with it using DuckDB syntax. This can be useful for testing migration queries locally before deploying them in Keboola.

Ask Kai

Ask anything about Keboola — I'll search the docs and cite the pages I use.