Mastering SQL Window Functions: Analyzing Sales Trends Over the Last 4 Quarters – Part 1

Mastering SQL Window Functions: Analyzing Sales Trends Over the Last 4 Quarters – Part 1

Introduction

When analyzing sales data, it's crucial to track performance trends over time. In this blog, we'll explore how to use SQL window functions to retrieve and analyze sales data for the current quarter and the last 3 quarters, providing insights such as:

Current and prior quarter sales

Next quarter sales (if available)

Rolling total of the last 4 quarters

Quarter-over-quarter sales change

We’ll break this down step by step with an easy-to-follow SQL query.


Step 1: Understanding the Dataset

Let’s use following tables from snowflake sample for this blog:

 

Article content

Our goal is to group the sales by quarters and analyze trends using window functions from STORE_SALES table for each individual ITEM_ID from ITEM table.

Our goal is to group the sales by quarters and analyze trends using window functions from STORE_SALES table for each individual ITEM_ID from ITEM table.


Step 2: Aggregating Sales Data by Quarter


Since sales are recorded daily, we first aggregate the total sales per year, quarter for each ITEM.

WITH QUARTERLY_SALES AS (

    SELECT

        IT.I_ITEM_ID AS ITEM_ID,

        D_YEAR AS SALE_YEAR,

        D_QOY AS SALE_QUARTER,

        SUM(SS_SALES_PRICE) AS TOTAL_SALES

    FROM SNOWFLAKE_SAMPLE_DATA.TPCDS_SF100TCL.STORE_SALES SS

    INNER JOIN     SNOWFLAKE_SAMPLE_DATA.TPCDS_SF100TCL.DATE_DIM DD ON SS_SOLD_DATE_SK = DD.D_DATE_SK

    INNER JOIN     SNOWFLAKE_SAMPLE_DATA.TPCDS_SF100TCL.ITEM IT ON SS.SS_ITEM_SK = IT.I_ITEM_SK

   

    GROUP BY ALL

)        

 

What This Does:

✅ Select year and quarter from Date Dimension.

✅ Aggregates sale_amount by item, year, and quarter.


Step 3: Using Window Functions to Analyze Sales Trends

Now, we use window functions to track quarter-over-quarter trends.

WITH QUARTERLY_SALES AS (

    SELECT

        IT.I_ITEM_ID AS ITEM_ID,

        D_YEAR AS SALE_YEAR,

        D_QOY AS SALE_QUARTER,

        SUM(SS_SALES_PRICE) AS TOTAL_SALES

    FROM SNOWFLAKE_SAMPLE_DATA.TPCDS_SF100TCL.STORE_SALES SS

    INNER JOIN     SNOWFLAKE_SAMPLE_DATA.TPCDS_SF100TCL.DATE_DIM DD ON SS_SOLD_DATE_SK = DD.D_DATE_SK

    INNER JOIN     SNOWFLAKE_SAMPLE_DATA.TPCDS_SF100TCL.ITEM IT ON SS.SS_ITEM_SK = IT.I_ITEM_SK

   

    GROUP BY ALL

),

-- SELECT TOP 10 * FROM QUARTERLY_SALES

 

RANKED_SALES AS (

    SELECT

        ITEM_ID,

        SALE_YEAR,

        SALE_QUARTER,

        TOTAL_SALES,

        ROW_NUMBER() OVER (PARTITION BY ITEM_ID ORDER BY SALE_YEAR DESC, SALE_QUARTER DESC) AS RNK,

       

        -- GET SALES FROM THE PREVIOUS QUARTER

        LAG(TOTAL_SALES, 1) OVER (PARTITION BY ITEM_ID ORDER BY SALE_YEAR DESC, SALE_QUARTER DESC) AS PRIOR_QUARTER_SALES,

 

        -- GET SALES FROM THE NEXT QUARTER (IF AVAILABLE)

        LEAD(TOTAL_SALES, 1) OVER (PARTITION BY ITEM_ID ORDER BY SALE_YEAR DESC, SALE_QUARTER DESC) AS NEXT_QUARTER_SALES,

 

        -- RUNNING TOTAL FOR THE LAST 4 QUARTERS

        SUM(TOTAL_SALES) OVER (PARTITION BY ITEM_ID ORDER BY SALE_YEAR DESC, SALE_QUARTER DESC ROWS BETWEEN 3 PRECEDING AND CURRENT ROW) AS RUNNING_4_QUARTERS_SALES,

 

        -- SALES CHANGE COMPARED TO PREVIOUS QUARTER

        TOTAL_SALES - LAG(TOTAL_SALES, 1) OVER (PARTITION BY ITEM_ID ORDER BY SALE_YEAR DESC, SALE_QUARTER DESC) AS QUARTER_SALES_CHANGE

    FROM QUARTERLY_SALES

)        

Key Window Functions Used:

LAG() → Retrieves sales from the previous quarter.

LEAD() → Retrieves sales from the next quarter.

SUM() OVER → Computes a rolling total for the last 4 quarters.

ROW_NUMBER() → Ranks quarters from most recent to oldest.


Step 4: Filtering the Last 4 Quarters

We now filter the results to only include the current quarter and the last 3 quarters.

SELECT

    ITEM_ID,

    SALE_YEAR,

    SALE_QUARTER,

    TOTAL_SALES,

    PRIOR_QUARTER_SALES,

    NEXT_QUARTER_SALES,

    RUNNING_4_QUARTERS_SALES,

    QUARTER_SALES_CHANGE

FROM RANKED_SALES

WHERE RNK <= 4  -- FETCH CURRENT AND LAST 3 QUARTERS

ORDER BY SALE_YEAR DESC, SALE_QUARTER DESC;        

What This Does:

✅ Limits results to the last 4 quarters.

✅ Sorts data from the latest quarter to the oldest.

 

Finally wrapping all the queries into a single view

 

CREATE VIEW VW_RANKED_QUARTERLY_SALES AS (

WITH QUARTERLY_SALES AS (

    SELECT

        IT.I_ITEM_ID AS ITEM_ID,

        D_YEAR AS SALE_YEAR,

        D_QOY AS SALE_QUARTER,

        SUM(SS_SALES_PRICE) AS TOTAL_SALES

    FROM SNOWFLAKE_SAMPLE_DATA.TPCDS_SF100TCL.STORE_SALES SS

    INNER JOIN     SNOWFLAKE_SAMPLE_DATA.TPCDS_SF100TCL.DATE_DIM DD ON SS_SOLD_DATE_SK = DD.D_DATE_SK

    INNER JOIN     SNOWFLAKE_SAMPLE_DATA.TPCDS_SF100TCL.ITEM IT ON SS.SS_ITEM_SK = IT.I_ITEM_SK

GROUP BY ALL

)

,

-- SELECT TOP 10 * FROM QUARTERLY_SALES

 

RANKED_SALES AS (

    SELECT

        ITEM_ID,

        SALE_YEAR,

        SALE_QUARTER,

        TOTAL_SALES,

        ROW_NUMBER() OVER (PARTITION BY ITEM_ID ORDER BY SALE_YEAR DESC, SALE_QUARTER DESC) AS RNK,

       

        -- GET SALES FROM THE PREVIOUS QUARTER

        LAG(TOTAL_SALES, 1) OVER (PARTITION BY ITEM_ID ORDER BY SALE_YEAR DESC, SALE_QUARTER DESC) AS PRIOR_QUARTER_SALES,

 

        -- GET SALES FROM THE NEXT QUARTER (IF AVAILABLE)

        LEAD(TOTAL_SALES, 1) OVER (PARTITION BY ITEM_ID ORDER BY SALE_YEAR DESC, SALE_QUARTER DESC) AS NEXT_QUARTER_SALES,

 

        -- RUNNING TOTAL FOR THE LAST 4 QUARTERS

        SUM(TOTAL_SALES) OVER (PARTITION BY ITEM_ID ORDER BY SALE_YEAR DESC, SALE_QUARTER DESC ROWS BETWEEN 3 PRECEDING AND CURRENT ROW) AS RUNNING_4_QUARTERS_SALES,

 
        -- SALES CHANGE COMPARED TO PREVIOUS QUARTER

        TOTAL_SALES - LAG(TOTAL_SALES, 1) OVER (PARTITION BY ITEM_ID ORDER BY SALE_YEAR DESC, SALE_QUARTER DESC) AS QUARTER_SALES_CHANGE

    FROM QUARTERLY_SALES

)

 

SELECT

    ITEM_ID,

    SALE_YEAR,

    SALE_QUARTER,

    TOTAL_SALES,

    PRIOR_QUARTER_SALES,

    NEXT_QUARTER_SALES,

    RUNNING_4_QUARTERS_SALES,

    QUARTER_SALES_CHANGE

FROM RANKED_SALES

WHERE RNK <= 4  -- FETCH CURRENT AND LAST 3 QUARTERS

ORDER BY SALE_YEAR DESC, SALE_QUARTER DESC

 

);
        

!Snowflake Developer Tip!

1)Use GROUP BY ALL instead of calling all columns in GROUP BY CLAUSE. Fast and easy way!

2)Use WindowFrame functions like Preceding/Following to traverse forward/backward for ROWS/RANGES        


View Lineage

Article content

Step 5: Example Output


Article content

Step 6: Key Business Insights

📌 Quarter-over-quarter trends: prior_quarter_sales and quarter_sales_change help identify growth or decline in sales.

📌 Predict future trends: next_quarter_sales provides insights into what might happen next. 📌 Rolling total performance: running_4_quarters_sales helps understand overall performance over time.


Conclusion

SQL window functions are powerful tools for analyzing sales trends over time. By leveraging LAG(), LEAD(), RANGE, ROWS and SUM() OVER, you can extract key insights for decision-making and forecasting.

Similarly, you can explore other WINDOW FUNCTIONS for advanced time series analysis. Check out the URL below for more details.

https://docs.snowflake.com/en/sql-reference/functions-window

Would you like to extend this further with year-over-year comparisons or cumulative moving averages? Let me know in the comments! 🚀





To view or add a comment, sign in

More articles by Karthikeyan Shanthakumar

Others also viewed

Explore content categories