generate_series() for Sequences and Date Spines in SQL
generate_series() is a PostgreSQL set-returning function that produces a sequence of values from a start point to an end point at a specified step interval. Its primary analytical use is generating a complete, gap-free sequence of dates or integers that can be joined to fact data to ensure every period appears in the result, even periods with no underlying activity.
generate_series() produces a sequence of values as rows — dates, timestamps, or integers — that you can query like a table. Its primary use in analytics is building a complete list of every date in a range, so that gaps in your data show up as zeros instead of disappearing from your results.
Here's the problem it solves. Your orders table only has rows for days when orders happened. Group by day and you get a result with no row for quiet days — they simply don't exist in the data. If you need those days to appear with a count of 0, there's nothing to GROUP BY. You need to create those rows synthetically.
generate_series() creates them. It takes a start, an end, and a step:
SELECT generate_series(
'2024-01-01'::date,
'2024-12-31'::date,
'1 day'::interval
) AS dayThis produces 366 rows, one per calendar day in 2024. The function expands into rows in FROM like a table. From here, the standard pattern is to wrap it in a CTE and LEFT JOIN to your fact data:
WITH date_spine AS (
SELECT generate_series(
'2024-01-01'::date,
'2024-12-31'::date,
'1 day'::interval
)::date AS day
)
SELECT
ds.day,
COALESCE(COUNT(o.id), 0) AS order_count
FROM date_spine ds
LEFT JOIN orders o ON o.ordered_at::date = ds.day
GROUP BY ds.day
ORDER BY ds.dayThe LEFT JOIN keeps every generated day regardless of whether orders exist. Days with no orders produce NULL in o.id, COUNT ignores NULLs, and COALESCE converts the resulting NULL count to 0. Every day in the range appears in the output.
Here's a monthly version you can run on the ecommerce data:
The one thing that trips people up
When you pass date inputs with an interval step, PostgreSQL returns timestamps, not dates. The ::date cast inside the CTE converts each value back to a plain date. Without it, the join on o.order_date (a date column) may fail or silently produce no matches because the types don't align.
The step controls the resolution. Change '1 day' to '1 week' or '1 month' for weekly or monthly spines. For monthly sequences, PostgreSQL applies calendar arithmetic: adding one month to January 31 gives February 28 (or 29 in a leap year), not March 2. The arithmetic follows real calendar logic, so month-end dates compress forward to the last day of the next month.
generate_series() works with integers too: generate_series(1, 100, 1) produces 100 rows numbered 1 through 100. The date spine pattern is the most common analytical use, but integer sequences appear in test data generation and row-numbering problems as well.
generate_series() in FROM vs in SELECT
You can call generate_series() directly in the FROM clause without wrapping it in a CTE, but the CTE approach is cleaner and gives the sequence a named column. Calling it in the SELECT list is also valid for simple cases: SELECT generate_series(1, 5) returns 5 rows with values 1 through 5. When used in FROM alongside other tables, always wrap it in a CTE or subquery with an explicit column alias — the raw function call produces a column named generate_series, which is awkward to join on.
10 generate_series() for Sequences and Date Spines practice problems
Write a query to return a sequence of integers from `1` through `5`, with each value appearing as a separate row.
Write a query to return the first day of each calendar month from January through June `2024`, one date per row.
Write a query to return a sequence covering every day from `'2024-01-08'` through `'2024-01-14'`, one date per row.
Write a query to return every date from `'2024-01-01'` through `'2024-01-07'` alongside the number of orders placed on that date. Days with no orders should appear with an order count of `0`.
Write a query to return every date in that five-day range and its total order revenue. Days with no orders should appear with a missing revenue value.
Write a query to return every date from `'2024-01-01'` through `'2024-01-07'` alongside the number of events recorded that day. Days with no events should appear with a count of `0`.
Write a query to return every integer from `1` through `10` alongside the customer name if a `customers` record with that `id` is on file, or a missing value if no record was found.
Write a query to return the total number of rows produced when `generate_series` is called with a start of `'2024-03-01'`, an end of `'2024-02-01'`, and a step of `1` day.
Write a query to return all renewal dates produced by `generate_series` with a start of `'2024-01-31'`, an end of `'2024-04-30'`, and a step of `'1 month'`. Each result should be cast back to `DATE`.
Write a query to return every date from `'2024-01-01'` through `'2024-01-14'` for which no order is on record.
These problems are part of the generate_series() for Sequences and Date Spines lesson in SQLMaxx, with instant grading and a worked solution on each.
Reading explains SQL. Writing it, over and over with instant feedback, is what makes you fluent.
That's the whole SQLMaxx loop: 600+ real problems, instant AI feedback, mastery you can actually see, and spaced review that won't let you forget.
Real problems. Not toy examples.
615 hand-built problems spanning all 66 concepts, from basic SELECTs to window functions, built on real schemas and real business questions, the kind you'll actually get asked on the job. Enough reps to make SQL automatic.
Write a query. Know if it's right in one second.
No copying an answer and hoping it clicked. The AI grader checks your real query against real data, catches exactly what's wrong, and explains the fix in plain English, like a senior analyst reading over your shoulder on every problem.
Stop guessing whether you actually know it.
SQLMaxx tracks every concept and shows you what you've mastered and what's still shaky. Your skills fill in one concept at a time, so 'I think I get joins' becomes something you can prove.
Learn it once. Keep it for good.
Most of what you learn this week fades by next week. So when a concept comes due for review, SQLMaxx hands you a fresh problem to solve from a blank editor, not a flashcard to re-read. A research-backed spaced-repetition algorithm (FSRS) times each return for right before you'd forget, so your SQL is still there months later, when the interview or the job actually needs it.
Practice, feedback, mastery, review. That's the loop that turns reading into real skill.
Start freeNo account, no credit card. Start solving in under a minute.