In this series we go through Snowflake SQL from the very first SELECT all the way to administration, one topic at a time. Every episode is a short written post with the queries, the real output, and a video you can follow along with.
There are 173 episodes across 13 chapters. Start at the top if you are new to Snowflake, or jump straight to the chapter you need - each post stands on its own.
Chapter 1 - Querying Data - SELECT, WHERE, ORDER BY
- SELECT Statement Explained - EXCLUDE, RENAME, REPLACE & ILIKE
- FROM Clause Explained - Table Aliases, Qualified Names & SELECT With No Table
- WHERE Clause Explained - Comparison Operators + The NULL Trap
- BETWEEN, IN, LIKE, ILIKE & RLIKE Explained
- AND, OR, NOT Explained - The Operator Precedence Mistake Everybody Makes
- NULL in Snowflake Explained - Why NOT IN Can Return ZERO Rows
- ORDER BY in Snowflake - NULLS FIRST, NULLS LAST & Why NULLs Don't Always Sort Last
- TOP 3 Is NOT the Top 3 - LIMIT, TOP, FETCH & OFFSET in Snowflake
- DISTINCT Is NOT a Function - Why COUNT(DISTINCT) Gave Me a Different Answer
- You CAN Use a Column Alias in WHERE - in Snowflake - Aliases & AS
- 7 divided by 2 Is NOT 3 - Operators, Precedence & Concatenation in Snowflake
- Why Your Query Returns ZERO Rows - Case Sensitivity in Snowflake Explained
Chapter 2 - Aggregation - GROUP BY and the aggregate functions
- COUNT(Star) vs COUNT(col) vs COUNT(DISTINCT) - Three Correct Answers, One Table
- SUM, AVG, MIN and MAX in Snowflake
- GROUP BY in Snowflake - Eight Rows In, Four Rows Out - The Whole Idea in One Picture
- GROUP BY ALL in Snowflake
- HAVING in Snowflake
- ROLLUP, CUBE and GROUPING SETS in Snowflake
- LISTAGG in Snowflake
- ARRAY_AGG and OBJECT_AGG in Snowflake
- MEDIAN, STDDEV and Percentiles in Snowflake
- Approximate Aggregates in Snowflake
- QUALIFY in Snowflake
Chapter 3 - Joins and Set Operators
- INNER JOIN in Snowflake
- LEFT and RIGHT OUTER JOIN in Snowflake
- FULL OUTER JOIN in Snowflake
- CROSS JOIN in Snowflake
- USING vs ON in Snowflake, and Why NATURAL JOIN Returned Zero Rows
- Joining Three or More Tables in Snowflake
- Self Joins in Snowflake
- Non-Equi Joins in Snowflake
- ASOF JOIN in Snowflake
- LATERAL in Snowflake
- UNION vs UNION ALL in Snowflake
- INTERSECT, MINUS and EXCEPT in Snowflake
- CONNECT BY in Snowflake
Chapter 4 - Subqueries and CTEs
- Scalar Subqueries in Snowflake
- The NOT IN NULL Trap in Snowflake - Correct SQL, No Error, Zero Rows
- EXISTS and NOT EXISTS in Snowflake - SELECT 1 vs SELECT Star
- ANY, ALL and SOME in Snowflake
- Correlated Subqueries in Snowflake
- Derived Tables in Snowflake - A Subquery in FROM Is a Table That Lives for One Query
- WITH & CTEs in Snowflake - The Same Query, Read Top to Bottom Instead of Inside Out
- Recursive CTEs in Snowflake - I Run the Recursion by Hand So You Can See 1 + 5 + 2 = 8
- VALUES in Snowflake - A Table Made of Literals
Chapter 5 - Window Functions
- OVER() in Snowflake - Aggregate Without Losing Your Rows
- PARTITION BY in Snowflake - The Same Groups as GROUP BY - Without Losing a Single Row
- ORDER BY Inside OVER in Snowflake - Three Words That Turn a Total Into a Running Total
- ROW_NUMBER in Snowflake
- RANK vs DENSE_RANK in Snowflake
- NTILE, PERCENT_RANK and CUME_DIST in Snowflake
- LAG and LEAD in Snowflake
- LAST_VALUE in Snowflake
- Window Frames in Snowflake - Measure the Frame WITH the Frame - and Watch It Move
- ROWS vs RANGE in Snowflake
- Moving Averages in Snowflake
- RATIO_TO_REPORT, WIDTH_BUCKET and CONDITIONAL_TRUE_EVENT
- Top-N Per Group in Snowflake
Chapter 6 - Reshaping and Sampling - PIVOT, UNPIVOT, MATCH_RECOGNIZE
- PIVOT in Snowflake - Rows Into Columns - and the One Mistake That Turns 8 Rows Into 41
- UNPIVOT in Snowflake - Why 8 Rows x 4 Columns Gives You 13 Rows, Not 32
- SAMPLE in Snowflake
- MATCH_RECOGNIZE in Snowflake
- AFTER MATCH SKIP in Snowflake - Same Pattern, Same 23 Rows, Three Different Answers
Chapter 7 - Built-in Functions - strings, numbers, dates, conversion
- CONCAT, Double Pipe and LENGTH in Snowflake - Strings 1
- TRIM, LTRIM, RTRIM, LPAD, RPAD in Snowflake - Strings 2
- SUBSTR, LEFT, RIGHT, POSITION, CHARINDEX - Strings 3
- REPLACE, TRANSLATE, SPLIT, SPLIT_PART, STRTOK - Strings 4
- REGEXP_LIKE and REGEXP_COUNT - Regex 1 (the anchoring rule)
- REGEXP_REPLACE, REGEXP_SUBSTR, REGEXP_INSTR - Regex 2
- ROUND, CEIL, FLOOR, TRUNC, ABS, SIGN, MOD - Numbers
- POWER, SQRT, EXP, LN, LOG, RANDOM, UNIFORM - Maths
- DATEADD, DATEDIFF, CURRENT_DATE - Dates 1
- DATE_TRUNC, DATE_PART, EXTRACT, LAST_DAY, NEXT_DAY - Dates 2
- DAYNAME, MONTHNAME, Weekday vs Weekend, TIME_SLICE - Dates 3
- TO_CHAR, TO_DATE, TRY_TO_DATE - Dates 4
- TIMESTAMP_NTZ vs LTZ vs TZ, CONVERT_TIMEZONE - Dates 5
- CAST, TO_NUMBER, TO_VARCHAR, TO_BOOLEAN - Conversion
- TRY_TO_NUMBER, TRY_CAST, TRY_TO_DATE - The TRY_ Family
- CASE WHEN, Simple vs Searched, NULL - The ELSE Trap
- IFF, NVL, NVL2, COALESCE, NULLIF, DECODE - Null Handling
- ZEROIFNULL, NULLIFZERO, GREATEST, LEAST, EQUAL_NULL
- CURRENT_USER, CURRENT_DATABASE, IDENTIFIER - Context Functions
- HASH, MD5, SHA2, BASE64, HEX - Hashing and Encoding
- BITAND, BITOR, BITXOR, BITNOT, Shifts - Bitwise Functions
- GENERATOR, SEQ4, Date Spine - Making Rows From Nothing
Chapter 8 - Semi-Structured Data - JSON, VARIANT, XML
- The VARIANT Type, TYPEOF, IS_NULL_VALUE - Semi-Structured Data
- PARSE_JSON, TRY_PARSE_JSON, CHECK_JSON, TO_JSON - Loading JSON
- Navigating JSON - Colon, Dots, Brackets, GET and GET_PATH
- Casting Out of VARIANT - Double Colon, TRY_TO_NUMBER, and why '1' is not 1
- LATERAL FLATTEN - Arrays into Rows, and the Total That Doubles
- FLATTEN Part 2 - RECURSIVE, MODE, PATH, and Nested Flattens
- ARRAY Functions - SIZE, CONTAINS, SLICE, CAT, SORT, AGG
- OBJECT Functions - CONSTRUCT, KEYS, INSERT, DELETE, PICK, AGG
- TYPEOF, IS_ and AS_ - The Safe VARIANT Cast at Last
- Structured Types - ARRAY(INT), OBJECT and MAP - It Refuses
- XML - PARSE_XML, CHECK_XML and XMLGET - Level 8 Finale
Chapter 9 - DDL - databases, schemas, tables, views
- Data Types - Which Names Are Lies - Level 9 Opens
- CREATE OR REPLACE DATABASE Is Destructive (and UNDROP Can't Save You)
- Schemas, and Dropping the PUBLIC Schema - Same Query, Different Rows
- CREATE TABLE - A DEFAULT Is Not a Fallback for NULL
- CTAS vs LIKE vs CLONE - What a CTAS Silently Throws Away
- A TEMPORARY Table Can Hide a PERMANENT One (Qualifying Won't Save You)
- ALTER TABLE - ADD, DROP, RENAME and SWAP WITH
- ALTER COLUMN - Three Rules, One Syntax (and a DEFAULT You Can't Change)
- PRIMARY KEY, UNIQUE and FOREIGN KEY Are Not Enforced - Level 9
- UNDROP Is Not A Stack, And TRUNCATE Does Not Reset AUTOINCREMENT
- CREATE OR ALTER TABLE Drops The Column You Leave Out
- A SELECT Star View Does Not Go Stale, It Breaks
- A Materialized View Is A Much Smaller Language
- Dynamic Tables Take the Query a Materialized View Refuses
- A Sequence Is Not In Order, And The Catalog Says So
- SHOW Is Not Queryable, Until You Wrap It - RESULT_SCAN
- External And Iceberg Tables - The Refusals Are The Specification
- How Do I Create An Index In Snowflake - The Answer Has Three Parts
Chapter 10 - DML - INSERT, UPDATE, DELETE, MERGE
- INSERT Matches By Position, Not By Name
- INSERT ALL Wrote 7 Rows From 5. INSERT FIRST Wrote 5
- UPDATE Has A Column That Says Your Join Matched Twice
- The DELETE That Emptied The Whole Table, And How I Got It Back
- MERGE - A Condition In The ON Clause Duplicated A Row
- MERGE Refuses The Obvious Dead Branch And Accepts The Subtle One
- The CREATE TABLE That Committed My Transaction
Chapter 11 - Loading and Pipelines - stages, COPY INTO, streams, tasks
- Two Of Your Stages Already Exist And You Never Created Either
- PUT Renames Your File And GET Hands It Back Still Zipped
- My PATTERN Matched Nothing And Snowflake Said Nothing
- The Same CSV File Read Twice, Giving Different Data
- I Ran The Same COPY INTO Twice And The Second One Did Nothing
- PARTIALLY_LOADED Is A Successful Load With Rows Missing
- COPY INTO Writes A Directory, Not A File
- INFER_SCHEMA Sized My Columns To Four Rows Of Data
- COPY FILES Won't Touch Your User Stage, And Doesn't Say Why
- My Snowpipe Loaded Six Rows Twice And COPY_HISTORY Told Me Why
- My Time Travel Repair Restored Six Rows And Still Got It Wrong
- The Catalog Listed My Dropped Table And UNDROP Said It Was Purged
- I Cloned A Schema And One View Kept Pointing At The Original
- A Stream Said It Had Data And Then Returned Zero Rows
- A Statement That Inserted Zero Rows Still Emptied My Stream
- I Ran A Task By Hand And The Table Was Still Empty
- I Resumed The Root And Snowflake Let Me Build A Dead DAG
- My Alert Fired Three Times For One Order And Would Not Stop
Chapter 12 - Programmability - UDFs and stored procedures
- A View That Returns Different Rows Depending On Who Is Looking
- My Function Said RETURNS INT And Handed Back 3.5
- My Comma Join To A Table Function Silently Dropped A Region
- My JavaScript UDF Could Not See Its Own Argument
- Python Kept My 30-Digit Number And JavaScript Could Not Even Declare It
- The RETURNS Clause My Function Ignored Is Enforced In A Procedure
- The Colon Only Matters In One Place, And This Is It
- My Loop Failed Half Way And Kept Every Row It Had Already Written
- My Cursor Walked Once And Then Gave Me Nothing
- I Caught The Exception And Every Row It Wrote Is Still There
- One Quote Mark Turned A Two Row Lookup Into Three
- A Procedure That Vanishes After One Statement, And The Row It Left Behind
- EXPLAIN Doesn't Run It, And I Counted The Rows To Prove It
- I Ran The Same Query Twice And Got 4 Then 1
- External Access Is Not Supported For Trial Accounts
Chapter 13 - Administration - warehouses, roles, security, usage
- CREATE WAREHOUSE Moved My Session And Broke My Own Demo
- Resizing A Suspended Warehouse Switched It On, And UNSET Gave Me NULL
- I Dropped A Resource Monitor That Was Guarding A Warehouse, And Snowflake Said Nothing
- A New Role Can Do Nothing, And Nothing Tells You So
- I Said A SELECT Needs Four Privileges. It Needs Three
- Revoking A Future Grant Doesn't Take Back What It Gave
- Snowflake Let Me Write A Grant That Does Absolutely Nothing
- SHOW GRANTS Gave Me A True Answer And A Wrong One
- Snowflake Let Me Point A User At A Role That Does Not Exist
- Snowflake Refused To Drop This One, And In Episode 157 It Did Not
- I Wrote The Masking Policy Myself And Snowflake Still Hid My Data From Me
- My Own Table Returned Zero Rows And Snowflake Called That Success
- I Removed A Value From A Tag's Allowed List And The Object Kept It
- The One Sharing Statement I Refused To Run, And Why
- One Statement Took A Role From 0 Grants To 673
- The Same COUNT Gave Me 76 And 64, And That Is The Whole Point
- ACCOUNT_USAGE Found The Table I Just Dropped. It Was The Wrong One
- One Parameter, Three Different Answers. ALTER SESSION Explained
- I Said These Would Be Refused. All Eight Ran. Replication Explained


