Lesson 4 of 4

Progress: 0/3 exercises solved (0%)

← PreviousFinal lesson

Solve all exercises below to unlock the next lesson.

Window frames — ROWS BETWEEN

Lesson Description

Use analytical SQL to rank, compare, and calculate trends across partitions without losing row-level detail.

Easy Project

Mini project: Build a compensation ranking dashboard query with department-level rank and delta from the top earner.

By default, aggregate window functions accumulate from the start of the partition to the current row. A frame clause lets you control exactly which rows are included.

Syntax

function() OVER (
  PARTITION BY col
  ORDER BY     col
  ROWS BETWEEN <start> AND <end>
)

Frame boundary keywords

KeywordMeaning
UNBOUNDED PRECEDINGFirst row of partition
n PRECEDINGn rows before current row
CURRENT ROWThe current row
n FOLLOWINGn rows after current row
UNBOUNDED FOLLOWINGLast row of partition

Running total (default frame)

SELECT
  hire_date, salary,
  SUM(salary) OVER (
    ORDER BY hire_date, id
    ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
  ) AS running_total
FROM employees
ORDER BY hire_date, id;

This is the default frame when ORDER BY is present — writing it explicitly is optional but clarifies intent.

3-row moving average

SELECT
  hire_date, salary,
  ROUND(AVG(salary) OVER (
    ORDER BY hire_date, id
    ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING
  ), 2) AS moving_avg_3
FROM employees
ORDER BY hire_date, id;

Full-partition aggregate (for ratio calculations)

SELECT
  department_id, salary,
  ROUND(
    salary / SUM(salary) OVER (
      PARTITION BY department_id
      ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
    ) * 100, 2
  ) AS pct_of_dept
FROM employees
ORDER BY department_id, salary DESC;

Default frame behavior

OVER() contentDefault frame
No ORDER BYEntire partition
Has ORDER BYUNBOUNDED PRECEDING TO CURRENT ROW

Tips

  • Use RANGE BETWEEN instead of ROWS BETWEEN when you want to include all rows with the same ORDER BY value as the current row.
  • ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING gives the full-partition total on every row — useful for percentage calculations.

Learning context

This lesson is part of Window Functions. Focus on the core idea in Window frames — ROWS BETWEEN, then validate with deliberate practice.

What to master

  • Understand clause order and intent
  • Write deterministic result queries
  • Use clear aliases and readable structure

Common mistakes

  • Skipping ORDER BY in final outputs
  • Using joins before validating base rows
  • Not checking row counts after changes

High-level strategy

  • Start simple, then layer complexity
  • Run often and compare expected shape
  • Refactor with CTEs when logic grows

Task ladder

  1. Baseline query: get a correct first output.
  2. Add one complication: edge case, join, filter, or ranking rule.
  3. Re-run and refine until output is stable and explainable.

Transparent data checks

  • Check row count before and after each major clause.
  • Validate one manual sample row for correctness.
  • Confirm final ordering and column naming for trust.

Retention loop

  • Run the solution once.
  • Rewrite from memory with one variation.
  • Explain the logic in plain English in one sentence.

Logical reasoning for commands

WHERE

Why: Limits rows to only the business-relevant subset.

Memory cue: Filter early to reduce noise.

ORDER BY

Why: Makes output deterministic and reviewable.

Memory cue: No ORDER BY means no guaranteed row order.

AS alias

Why: Makes output columns readable for teams and reports.

Memory cue: If the name is clear, the query is easier to trust.

Concept check

Quick check: Window frames — ROWS BETWEEN

3 quick questions. One at a time. Instant score at the end.

Exercise 1

Running salary total by hire date

Enterprise Workforce Analyticseasy

Return first_name, last_name, hire_date, salary, and running_total — a cumulative sum of salary ordered by hire_date, then id. Use ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW.

Run a query to see results here.
Exercise 2

3-employee moving average salary

Enterprise Workforce Analyticseasy

Return first_name, last_name, hire_date, salary, and moving_avg — the average salary across the current employee and 1 row before and 1 row after (ordered by hire_date, id). Round to 2 decimal places.

Run a query to see results here.
Exercise 3

Salary as % of department total

Enterprise Workforce Analyticseasy

Return first_name, last_name, department_id, salary, and pct_of_dept — each employee's salary as a percentage of their department's total salary. Use ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING. Round to 2 decimal places. Order by department_id, salary descending.

Run a query to see results here.

Final checkpoint

End-of-course high-level quiz

Pass mark: 80%. Review each explanation after you submit.

1. Which clause should you use to filter grouped results?

2. What is the safest way to avoid accidental row multiplication in joins?

3. Which feature is best for ranking rows within partitions?

4. Why should final analytical queries usually include ORDER BY?

5. When query logic gets complex, what improves readability most?

Lesson 4 of 4

0/3 solved (0%)

← PreviousFinal lesson

Finish this lesson to unlock next.