SQL: Querying, Filtering, Joins, and Window Functions
Master Snowflake SQL with practical examples on querying, filtering, joins, and window functions.
20+ years shipping high-throughput database systems. Notes here come from systems that actually shipped.
- ✓Basic understanding of SQL (SELECT, WHERE, JOIN, GROUP BY).
- ✓Familiarity with data warehousing concepts.
- ✓Access to a Snowflake account or trial environment.
- Snowflake SQL extends standard SQL with features like time travel, zero-copy cloning, and semi-structured data support.
- Filtering uses standard WHERE clause but benefits from automatic clustering and micro-partition pruning.
- Joins in Snowflake are similar to standard SQL but optimized for large-scale data with automatic statistics.
- Window functions are fully supported with efficient execution on massive datasets.
Think of Snowflake as a super-powered library where books (data) are stored in a magical way that makes finding any book incredibly fast, no matter how many books there are. You can ask questions (queries) using a special language (SQL) that works almost exactly like the library's standard card catalog, but with extra tricks like looking at how books were organized yesterday (time travel) or making instant copies without using extra shelf space (zero-copy cloning).
Snowflake is a cloud-native data warehouse that has revolutionized how organizations handle large-scale analytics. While it uses standard SQL, Snowflake introduces unique features that enhance performance, scalability, and ease of use. This tutorial focuses on the core SQL operations you'll use daily: querying, filtering, joins, and window functions. You'll learn not just the syntax, but how to write efficient queries that leverage Snowflake's architecture. We'll start with basic SELECT statements and filtering, then move to joins across multiple tables, and finally dive into window functions for analytical queries. Each section includes production-ready examples and common pitfalls. By the end, you'll be able to write complex analytical queries that run efficiently on terabytes of data.
Basic Querying with SELECT
Snowflake's SELECT statement follows standard SQL but includes powerful extensions. Start with simple queries to explore your data. Use SELECT to see all columns, but in production, always specify columns to reduce I/O. Snowflake's columnar storage makes SELECT efficient only when you need all columns. Use LIMIT to preview data. Snowflake supports TOP and LIMIT, but LIMIT is more common. Example: SELECT order_id, customer_id, order_date FROM orders LIMIT 10;
Filtering with WHERE
Filtering in Snowflake uses the standard WHERE clause with predicates. Snowflake's micro-partition pruning automatically skips irrelevant partitions based on the filter conditions. For optimal performance, filter on columns that are used in the clustering key. Use comparison operators, IN, BETWEEN, LIKE, and IS NULL. Snowflake also supports QUALIFY for filtering after window functions. Example: SELECT * FROM orders WHERE order_date >= '2024-01-01' AND order_date < '2024-02-01';
Joins in Snowflake
Snowflake supports all standard join types: INNER, LEFT, RIGHT, FULL OUTER, CROSS, and NATURAL joins. It also supports LATERAL joins and ASOF joins for time-series data. For large tables, ensure join columns are of the same data type to avoid implicit casts. Snowflake's query optimizer automatically chooses join order and algorithm (hash join, merge join, etc.). Use EXPLAIN to see the join strategy. Example: SELECT c.customer_name, o.order_id, o.total_amount FROM customers c INNER JOIN orders o ON c.customer_id = o.customer_id;
Window Functions: ROW_NUMBER, RANK, DENSE_RANK
Window functions perform calculations across a set of rows related to the current row. They are essential for analytical queries. Snowflake supports all standard window functions: ROW_NUMBER, RANK, DENSE_RANK, NTILE, LEAD, LAG, FIRST_VALUE, LAST_VALUE, and aggregate functions with OVER clause. The PARTITION BY clause divides the result set into partitions, and ORDER BY defines the order within each partition. Example: SELECT customer_id, order_date, total_amount, ROW_NUMBER() OVER (PARTITION BY customer_id ORDER BY order_date DESC) AS rn FROM orders;
Aggregate Functions with OVER: Running Totals and Moving Averages
Aggregate functions like SUM, AVG, COUNT can be used with OVER to compute running totals, moving averages, and cumulative statistics. The frame clause (ROWS BETWEEN) defines which rows to include. Snowflake supports ROWS, RANGE, and GROUPS frames. Example: SELECT order_date, total_amount, SUM(total_amount) OVER (ORDER BY order_date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS running_total FROM orders;
Time Travel and Zero-Copy Cloning
Snowflake's Time Travel allows you to query historical data up to 90 days back (depending on edition). Use AT or BEFORE with a timestamp, offset, or query ID. Zero-copy cloning creates a copy of a table without duplicating data, useful for testing and backups. Example: SELECT * FROM orders AT (TIMESTAMP => '2024-01-15 10:00:00'::TIMESTAMP); CREATE OR REPLACE TABLE orders_backup CLONE orders;
Best Practices for Performance
To get the best performance from Snowflake SQL: 1) Use clustering keys on large tables that are frequently filtered by certain columns. 2) Avoid SELECT * in production; specify only needed columns. 3) Use EXPLAIN to understand query plans. 4) Leverage materialized views for pre-aggregated data. 5) Use appropriate warehouse size; auto-suspend to save costs. 6) Use QUALIFY instead of subqueries for window function filtering. 7) Use CTEs for readability but be aware they are materialized differently in Snowflake.
The Case of the Disappearing Rows: Time Travel to the Rescue
- Always use transactions and test DELETE statements with SELECT first.
- Enable Time Travel retention (default 1 day, can be extended).
- Use zero-copy cloning for instant backups before risky operations.
- Implement role-based access control to limit destructive operations.
- Set up automated snapshots or use Snowflake's fail-safe for long-term recovery.
SELECT * FROM INFORMATION_SCHEMA.QUERY_HISTORY WHERE QUERY_ID = '<id>';ALTER WAREHOUSE my_wh SET WAREHOUSE_SIZE = 'X-LARGE';| File | Command / Code | Purpose |
|---|---|---|
| basic_select.sql | SELECT order_id, customer_id, order_date | Basic Querying with SELECT |
| filtering.sql | SELECT order_id, customer_id, order_date, total_amount | Filtering with WHERE |
| joins.sql | SELECT c.customer_name, COUNT(o.order_id) AS order_count, SUM(o.total_amount) AS... | Joins in Snowflake |
| window_functions.sql | SELECT customer_id, DATE_TRUNC('month', order_date) AS month, | Window Functions |
| running_total.sql | SELECT order_date, daily_total, | Aggregate Functions with OVER |
| time_travel.sql | SELECT * FROM orders AT (OFFSET => -3600); | Time Travel and Zero-Copy Cloning |
| best_practices.sql | WITH monthly_sales AS ( | Best Practices for Performance |
Key takeaways
Common mistakes to avoid
3 patternsUsing SELECT * in production queries
Forgetting to use QUALIFY and using subqueries instead
Joining on columns with different data types
Interview Questions on This Topic
Explain the difference between ROW_NUMBER, RANK, and DENSE_RANK.
Frequently Asked Questions
20+ years shipping high-throughput database systems. Notes here come from systems that actually shipped.
That's Snowflake. Mark it forged?
3 min read · try the examples if you haven't