Build Advanced SQL Window Functions
Skill for building SQL window functions: ranking, running totals, moving averages, and cross-platform patterns.
1.0.0Add to Favorites
Why it matters
Leverage expert SQL window function capabilities to perform complex analytical queries, ranking, aggregation, and offset operations across diverse modern databases.
Outcomes
What it gets done
Construct efficient window functions for ranking and sequential analysis.
Implement running calculations and moving averages with precise frame specifications.
Optimize window function performance through indexing and advanced techniques.
Ensure cross-platform compatibility and leverage database-specific features.
Install
Add it to your toolbox
Run in your project directory:
curl -fsSL https://spark.entire.vc/get/vb-window-function-builder | bash Overview
Window Function Builder
A skill for writing SQL window functions covering ranking, running totals, moving averages, and cross-platform syntax like BigQuery/Snowflake QUALIFY and SQL Server WITHIN GROUP. Use for analytical SQL needing ranking, running totals, or period comparisons. Assumes standard window function support - pre-8.0 MySQL needs simplified patterns.
What it does
Window Function Builder is a skill for creating optimized SQL window functions for analytics, ranking, running calculations, and business intelligence queries across PostgreSQL, SQL Server, Oracle, MySQL, BigQuery, and Snowflake. It's built on the essential window syntax - FUNCTION() OVER (PARTITION BY ... ORDER BY ... ROWS/RANGE BETWEEN ...) - and covers four function categories: ranking (ROW_NUMBER, RANK, DENSE_RANK, NTILE), aggregate (SUM/AVG/COUNT/MIN/MAX with frames), offset (LAG, LEAD, FIRST_VALUE, LAST_VALUE), and statistical (PERCENT_RANK, CUME_DIST, PERCENTILE_CONT).
For ranking and sequential analysis, it shows multi-level ranking with tie handling (ROW_NUMBER vs RANK vs DENSE_RANK vs NTILE quartiles in one query) and a top-N-per-group pattern combining ROW_NUMBER with PERCENT_RANK to select both an absolute top-5 and a percentile-based cutoff.
SELECT
date,
daily_sales,
SUM(daily_sales) OVER (
ORDER BY date
ROWS UNBOUNDED PRECEDING
) as running_total,
AVG(daily_sales) OVER (
ORDER BY date
ROWS BETWEEN 6 PRECEDING AND CURRENT ROW
) as seven_day_avg
FROM daily_sales
ORDER BY date;
For running calculations, it covers running totals and moving averages with ROWS BETWEEN frame clauses, and year-over-year comparison using LAG with a 12-period offset plus NULLIF-guarded percent-change calculation. Advanced frame specifications cover RANGE-based time windows (e.g. a rolling 30-day average using RANGE BETWEEN INTERVAL) versus ROWS-based windows, and FIRST_VALUE/LAST_VALUE across an unbounded frame to pull partition boundary values.
For complex business calculations, it demonstrates a customer lifecycle analysis combining ROW_NUMBER (order sequence), LAG (previous order date), and a running SUM (lifetime value) to classify each order as First Order, Reactivated (gap over 365 days), or Repeat.
For performance, it recommends composite indexes on PARTITION BY and ORDER BY columns, covering indexes for frequently accessed columns, and monitoring execution plans for sort operations - plus a CTE-based pattern that computes window functions once in a base CTE and reuses the results in the outer query rather than recomputing them.
Cross-platform considerations map feature support and syntax differences: PostgreSQL has full standard compliance and all frame types; SQL Server supports OFFSET/FETCH with window functions and WITHIN GROUP for ordered aggregation; BigQuery and Snowflake support the QUALIFY clause for filtering directly on window function results without a wrapping CTE; MySQL 8.0+ has basic window functions with limited frame support. It shows the BigQuery/Snowflake QUALIFY shortcut for a common top-1-per-group filter and SQL Server's WITHIN GROUP for ordered string aggregation.
Best practices include using appropriate partitioning to avoid oversized window operations, always specifying explicit frame clauses with aggregate functions, handling NULLs explicitly with COALESCE/ISNULL, testing edge cases like empty partitions and single-row groups, using CTEs to keep complex window logic readable, profiling and adjusting partitioning strategy based on real performance, and leveraging platform-specific optimizations like QUALIFY where available.
When to use - and when NOT to
Use this skill when writing analytical SQL that needs ranking, running totals, moving averages, period-over-period comparisons, or top-N-per-group logic - reporting queries, BI dashboards, or customer/product lifecycle analysis built directly in SQL.
It assumes the target database supports standard window function syntax; MySQL versions before 8.0 and databases with limited frame support will need simplified patterns rather than the full RANGE-based or advanced frame specifications shown here.
Inputs and outputs
Inputs: the target database platform and the analytical question to answer (ranking, running total, period comparison, cohort/lifecycle segmentation).
Outputs: optimized SQL queries using appropriate window functions, frame clauses, and platform-specific syntax (QUALIFY, WITHIN GROUP) tuned to the target database.
Integrations
Targets PostgreSQL, SQL Server, Oracle, MySQL 8.0+, BigQuery, and Snowflake, using each platform's native window function syntax and extensions.
Who it's for
Data analysts, BI developers, and backend engineers writing analytical SQL who need correct, performant window function patterns across different database platforms.
Source README
Window Function Builder Expert
You are an expert in SQL window functions with deep knowledge of analytical query patterns, performance optimization, and cross-platform compatibility. You excel at creating efficient window functions for ranking, aggregation, offset operations, and complex business calculations across PostgreSQL, SQL Server, Oracle, MySQL, BigQuery, Snowflake, and other modern databases.
Core Window Function Components
Essential Syntax Structure
FUNCTION() OVER (PARTITION BY ... ORDER BY ... ROWS/RANGE BETWEEN ...)- Partition clause defines calculation groups
- Order clause determines row sequence for calculations
- Frame clause specifies which rows to include in calculations
- Always consider NULL handling and edge cases
Function Categories
- Ranking: ROW_NUMBER(), RANK(), DENSE_RANK(), NTILE()
- Aggregate: SUM(), AVG(), COUNT(), MIN(), MAX() with frames
- Offset: LAG(), LEAD(), FIRST_VALUE(), LAST_VALUE()
- Statistical: PERCENT_RANK(), CUME_DIST(), PERCENTILE_CONT()
Ranking and Sequential Analysis
-- Multi-level ranking with ties handling
SELECT
product_id,
category,
sales_amount,
ROW_NUMBER() OVER (PARTITION BY category ORDER BY sales_amount DESC) as row_num,
RANK() OVER (PARTITION BY category ORDER BY sales_amount DESC) as rank_with_gaps,
DENSE_RANK() OVER (PARTITION BY category ORDER BY sales_amount DESC) as dense_rank,
NTILE(4) OVER (PARTITION BY category ORDER BY sales_amount DESC) as quartile
FROM sales_data;
-- Top N per group with percentage
WITH ranked_sales AS (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY region ORDER BY revenue DESC) as rn,
PERCENT_RANK() OVER (PARTITION BY region ORDER BY revenue DESC) as pct_rank
FROM sales_summary
)
SELECT * FROM ranked_sales
WHERE rn <= 5 AND pct_rank <= 0.1;
Running Calculations and Moving Averages
-- Running totals and moving averages
SELECT
date,
daily_sales,
SUM(daily_sales) OVER (
ORDER BY date
ROWS UNBOUNDED PRECEDING
) as running_total,
AVG(daily_sales) OVER (
ORDER BY date
ROWS BETWEEN 6 PRECEDING AND CURRENT ROW
) as seven_day_avg,
SUM(daily_sales) OVER (
ORDER BY date
ROWS BETWEEN CURRENT ROW AND 2 FOLLOWING
) as next_three_days
FROM daily_sales
ORDER BY date;
-- Year-over-year comparison with LAG
SELECT
year,
month,
revenue,
LAG(revenue, 12) OVER (ORDER BY year, month) as prev_year_revenue,
revenue - LAG(revenue, 12) OVER (ORDER BY year, month) as yoy_change,
ROUND(
(revenue - LAG(revenue, 12) OVER (ORDER BY year, month)) * 100.0 /
NULLIF(LAG(revenue, 12) OVER (ORDER BY year, month), 0), 2
) as yoy_pct_change
FROM monthly_revenue;
Advanced Frame Specifications
-- Range-based windows for time series
SELECT
timestamp,
value,
-- Last 30 days using RANGE
AVG(value) OVER (
ORDER BY timestamp
RANGE BETWEEN INTERVAL '30' DAY PRECEDING
AND CURRENT ROW
) as avg_30_days,
-- First and last values in partition
FIRST_VALUE(value) OVER (
PARTITION BY category
ORDER BY timestamp
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
) as first_value,
LAST_VALUE(value) OVER (
PARTITION BY category
ORDER BY timestamp
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
) as last_value
FROM time_series_data;
Complex Business Calculations
-- Customer lifecycle analysis
WITH customer_orders AS (
SELECT
customer_id,
order_date,
order_amount,
ROW_NUMBER() OVER (PARTITION BY customer_id ORDER BY order_date) as order_sequence,
LAG(order_date) OVER (PARTITION BY customer_id ORDER BY order_date) as prev_order_date,
SUM(order_amount) OVER (
PARTITION BY customer_id
ORDER BY order_date
ROWS UNBOUNDED PRECEDING
) as lifetime_value
FROM orders
)
SELECT
customer_id,
order_date,
order_amount,
lifetime_value,
CASE
WHEN order_sequence = 1 THEN 'First Order'
WHEN DATEDIFF(day, prev_order_date, order_date) > 365 THEN 'Reactivated'
ELSE 'Repeat'
END as order_type
FROM customer_orders;
Performance Optimization
Indexing Strategy
- Create composite indexes on PARTITION BY and ORDER BY columns
- Consider covering indexes for frequently accessed columns
- Monitor execution plans for sort operations
Optimization Techniques
-- Efficient window function reuse
WITH base_windows AS (
SELECT
*,
SUM(amount) OVER (PARTITION BY category ORDER BY date) as running_sum,
ROW_NUMBER() OVER (PARTITION BY category ORDER BY date DESC) as rn
FROM transactions
)
SELECT
*,
running_sum,
CASE WHEN rn = 1 THEN 'Latest' ELSE 'Historical' END as status
FROM base_windows;
Cross-Platform Considerations
Database-Specific Features
- PostgreSQL: Full standard compliance, supports all frame types
- SQL Server: OFFSET/FETCH with window functions, WITHIN GROUP
- BigQuery: QUALIFY clause for filtering window results
- Snowflake: QUALIFY, window function chaining
- MySQL 8.0+: Basic window functions, limited frame support
Common Patterns
-- BigQuery QUALIFY syntax
SELECT customer_id, order_date, amount
FROM orders
QUALIFY ROW_NUMBER() OVER (PARTITION BY customer_id ORDER BY order_date DESC) = 1;
-- SQL Server WITHIN GROUP
SELECT
department,
STRING_AGG(employee_name, ', ') WITHIN GROUP (ORDER BY salary DESC) as top_earners
FROM employees
GROUP BY department;
Best Practices
- Use appropriate partitioning to avoid large window operations
- Specify explicit frame clauses when using aggregate functions
- Consider NULL handling with COALESCE or ISNULL in calculations
- Test window functions with edge cases (empty partitions, single rows)
- Use CTEs to make complex window operations readable
- Profile query performance and adjust partitioning strategy accordingly
- Leverage database-specific optimizations (QUALIFY, specialized functions)
- Document business logic clearly when using complex frame specifications
Discussion
Questions & comments · 0
Sign In Sign in to leave a comment.