Snowflake Cheat Sheet

Snowflake SQL, grouped by task. Two habits save the most time here: unquoted identifiers fold to upper case, so a column created as "total" is not the same as total, and almost every schema change is metadata-only, so ALTER runs instantly on tables of any size. The reference marks where that stops being true.

Tables and columns

CREATE OR REPLACE TABLE orders (
  id NUMBER AUTOINCREMENT,
  customer_id NUMBER,
  total NUMBER(10,2),
  created_at TIMESTAMP_NTZ DEFAULT CURRENT_TIMESTAMP()
);
Create a table from a query
CREATE OR REPLACE TABLE recent_orders AS
SELECT * FROM orders WHERE created_at > DATEADD(day, -30, CURRENT_TIMESTAMP());
CREATE TABLE orders_backup CLONE orders;
ALTER TABLE orders RENAME TO customer_orders;
ALTER TABLE orders ADD COLUMN status VARCHAR DEFAULT 'new';
ALTER TABLE orders RENAME COLUMN total TO amount;
ALTER TABLE orders DROP COLUMN status;
ALTER TABLE orders ALTER COLUMN total SET DATA TYPE NUMBER(12,2);
TRUNCATE TABLE orders;
DROP TABLE IF EXISTS orders_backup;
UNDROP TABLE orders_backup;  -- within the Time Travel window
Read a table as it was
SELECT * FROM orders AT (OFFSET => -60 * 60);  -- one hour ago
DESC TABLE orders;
COMMENT ON TABLE orders IS 'One row per customer order.';

Rows and data

INSERT INTO orders (customer_id, total)
VALUES (1, 49.90), (2, 12.00);
UPDATE orders SET status = 'shipped' WHERE id = 42;
Update from another table
UPDATE orders o
SET total = t.amount
FROM order_totals t
WHERE t.order_id = o.id;
DELETE FROM orders WHERE created_at < DATEADD(year, -2, CURRENT_TIMESTAMP());
MERGE INTO orders t
USING orders_staging s ON t.id = s.id
WHEN MATCHED THEN UPDATE SET t.total = s.total
WHEN NOT MATCHED THEN INSERT (id, total) VALUES (s.id, s.total);
CREATE OR REPLACE TABLE orders AS
SELECT * FROM orders
QUALIFY ROW_NUMBER() OVER (PARTITION BY id ORDER BY created_at DESC) = 1;
Load a file from a stage
COPY INTO orders
FROM @my_stage/orders/
FILE_FORMAT = (TYPE = CSV SKIP_HEADER = 1);

Querying, dates and strings

CREATE OR REPLACE VIEW recent_orders AS
SELECT * FROM orders WHERE created_at > DATEADD(day, -30, CURRENT_TIMESTAMP());
SELECT * FROM orders
QUALIFY ROW_NUMBER() OVER (PARTITION BY customer_id ORDER BY created_at DESC) = 1;
SELECT total::VARCHAR,
       TRY_CAST(raw_amount AS NUMBER)  -- NULL instead of an error
FROM orders;
SELECT CURRENT_DATE(), CURRENT_TIMESTAMP(), SYSDATE();
SELECT DATEADD(day, 7, created_at)                 AS due,
       DATEDIFF(hour, created_at, delivered_at)    AS hours
FROM orders;
Group by week or month
SELECT DATE_TRUNC('month', created_at) AS month, COUNT(*)
FROM orders GROUP BY 1 ORDER BY 1;
Format a date as text
SELECT TO_CHAR(created_at, 'YYYY-MM-DD HH24:MI') FROM orders;
SELECT CONVERT_TIMEZONE('UTC', 'Europe/Helsinki', created_at) FROM orders;
SELECT e.value:sku::VARCHAR AS sku
FROM orders o, LATERAL FLATTEN(input => o.payload:items) e;
SELECT SPLIT_PART(email, '@', 2) AS domain,
       SPLIT(tags, ',')          AS tag_array
FROM customers;
SELECT customer_id, LISTAGG(sku, ', ') WITHIN GROUP (ORDER BY sku)
FROM order_items GROUP BY customer_id;
SELECT * FROM (SELECT customer_id, status, total FROM orders)
PIVOT (SUM(total) FOR status IN ('new', 'shipped', 'cancelled'));

Schemas, sequences and access

Set the working context
USE WAREHOUSE analytics_wh;
USE DATABASE analytics;
USE SCHEMA public;
SHOW TABLES;

SELECT table_name FROM information_schema.tables
WHERE table_schema = CURRENT_SCHEMA();
CREATE SCHEMA IF NOT EXISTS analytics.staging;
CREATE SEQUENCE order_seq START = 1 INCREMENT = 1;
SELECT order_seq.NEXTVAL;
GRANT USAGE ON SCHEMA analytics.public TO ROLE analyst;
GRANT SELECT ON ALL TABLES IN SCHEMA analytics.public TO ROLE analyst;
Table size and row count
SELECT table_name, row_count, bytes / POWER(1024, 3) AS gb
FROM information_schema.tables
WHERE table_schema = CURRENT_SCHEMA();
What a query cost
SELECT query_text, total_elapsed_time / 1000 AS seconds, bytes_scanned
FROM TABLE(information_schema.query_history())
ORDER BY start_time DESC LIMIT 20;
Cancel a running query
SELECT SYSTEM$CANCEL_QUERY('01a2b3c4-0000-0000-0000-000000000000');
Current context
SELECT CURRENT_VERSION(), CURRENT_ROLE(), CURRENT_WAREHOUSE(), CURRENT_DATABASE();

All 30 Snowflake how-to guides

Each guide is a short answer with examples you can copy and run, plus the gotchas and errors that come with it.

Run Snowflake queries without a desktop client

SaturnSQL is a browser-based SQL editor for teams: shared query library, schema-aware autocomplete, and scheduled exports to Google Sheets and Slack.

Try it free

The same tasks in other databases