Featured image of post Minding the Gap (Date) with Window Functions

Minding the Gap (Date) with Window Functions

Using SQL Window Functions to identify and analyze gaps in sequential data.

Introduction

In this post, we explore the concept of gaps in sequential data and how Window Functions can be leveraged to identify and analyze them effectively.

Refresher on Window Functions

Window Functions were introduced in ANSI SQL:2003. These functions allow computations across a set of table rows that are related to the current row using the OVER clause.

A common use case for window functions is analytical queries, particularly in identifying islands and gaps in datasets:

  • Islands: Groups of continuous data sequences.
  • Gaps: Missing sequences between data points.

Problem Scenario

Consider a scenario where a patient enrolled in a health program is required to check in daily. Our objective is to identify the gaps in their consecutive visits.

Dataset

Sample Data

visit_date
2025-02-02
2025-02-03
2025-02-11
2025-02-12
2025-02-13
2025-02-27
2025-04-03
2025-04-04
2025-04-05
2025-05-12

Creating the Dataset in SQL

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
DROP TABLE IF EXISTS #table2;

CREATE TABLE #table2(visit_date DATE);

INSERT INTO #table2 (visit_date)
VALUES
    ('2025-02-02'),
    ('2025-02-03'),
    ('2025-02-11'),
    ('2025-02-12'),
    ('2025-02-13'),
    ('2025-02-27'),
    ('2025-04-03'),
    ('2025-04-04'),
    ('2025-04-05'),
    ('2025-05-12');

Expected Output

After analyzing gaps, we should obtain the following missing date ranges:

start_range end_range
2025-02-04 2025-02-10
2025-02-14 2025-02-26
2025-02-28 2025-04-02
2025-04-06 2025-05-11

Algorithmic Approach

To identify gaps, we calculate the difference in days between each visit. If the gap is greater than one day, it indicates a missing period.

Step 1: Compare Each Visit to the Next

current_visit next_visit
2025-02-02 2025-02-03
2025-02-03 2025-02-11
2025-02-11 2025-02-12
2025-02-12 2025-02-13
2025-02-13 2025-02-27
2025-02-27 2025-04-03
2025-04-03 2025-04-04
2025-04-04 2025-04-05
2025-04-05 2025-05-12
2025-05-12 NULL

Step 2: Compute Days Between Visits

current_visit next_visit days_between
2025-02-02 2025-02-03 1
2025-02-03 2025-02-11 8
2025-02-11 2025-02-12 1
2025-02-12 2025-02-13 1
2025-02-13 2025-02-27 14
2025-02-27 2025-04-03 35
2025-04-03 2025-04-04 1
2025-04-04 2025-04-05 1
2025-04-05 2025-05-12 37
2025-05-12 NULL NULL

Any difference greater than 1 day represents a gap.

SQL Implementation

We use the LEAD Window Function to fetch the next visit date for comparison.

1
2
3
SELECT visit_date AS startDate,
       LEAD(visit_date) OVER (ORDER BY visit_date) AS endDate
FROM #table2;

Output

startDate endDate
2025-02-02 2025-02-03
2025-02-03 2025-02-11
2025-02-11 2025-02-12
2025-02-12 2025-02-13
2025-02-13 2025-02-27
2025-02-27 2025-04-03
2025-04-03 2025-04-04
2025-04-04 2025-04-05
2025-04-05 2025-05-12
2025-05-12 NULL

Final Query

The final query identifies gaps by filtering where the difference is greater than 1 day:

1
2
3
4
5
6
7
8
9
WITH CTE_LEAD AS (
    SELECT visit_date AS startDate,
           LEAD(visit_date) OVER (ORDER BY visit_date) AS endDate
    FROM #table2
)
SELECT DATEADD(DAY, 1, startDate) AS startDate,
       DATEADD(DAY, -1, endDate) AS endDate
FROM CTE_LEAD
WHERE DATEDIFF(DAY, startDate, endDate) > 1;

Explanation

  • The CTE_LEAD common table expression fetches the next visit date.
  • The outer query filters out rows where the gap is more than one day.
  • The DATEADD() function adjusts the start and end dates to reflect the missing range.

Final Output

startDate endDate
2025-02-04 2025-02-10
2025-02-14 2025-02-26
2025-02-28 2025-04-02
2025-04-06 2025-05-11

Conclusion

This post demonstrated how Window Functions help identify gaps in sequential data efficiently, making them a powerful tool for analytical SQL queries.

References

Desiderata by Max Ehrmann

Go placidly amid the noise and the haste, and remember what peace there may be in silence. As far as possible without surrender be on good terms with all persons. Speak your truth quietly and clearly; and listen to others, even to the dull and the ignorant, they too have their story. Avoid loud and aggressive persons, they are vexations to the spirit.

If you compare yourself with others, you may become vain or bitter; for always there will be greater and lesser persons than yourself. Enjoy your achievements as well as your plans. Keep interested in your own career, however humble; it is a real possession in the changing fortunes of time.

Exercise caution in your business affairs, for the world is full of trickery. But let not this blind you to what virtue there is; many persons strive for high ideals, and everywhere life is full of heroism. Be yourself. Especially do not feign affection. Neither be cynical about love; for in the face of all aridity and disenchantment it is as perennial as the grass. Take kindly the counsel of the years, gracefully surrendering the things of youth.

Nurture strength of spirit to shield you in sudden misfortune. But do not distress yourself with dark imaginings. Many fears are born of fatigue and loneliness. Beyond a wholesome discipline, be gentle with yourself. You are a child of the universe, no less than the trees and the stars; you have a right to be here. And whether or not it is clear to you, no doubt the universe is unfolding as it should.

Therefore, be at peace with God, whatever you conceive Him to be. And whatever your labors and aspirations in the noisy confusion of life, keep peace in your soul. With all its sham, drudgery and broken dreams; it is still a beautiful world. Be cheerful.

Strive to be happy.

Built with Hugo
Theme Stack designed by Jimmy