---
title: "GENERATE_SERIES"
description: "GENERATE_SERIES(start, stop, step) produces a sequence of numeric, date, or timestamp values, commonly used to build calendars or fill gaps in time-series data."
canonical: "https://motherduck.com/glossary/generate-series/"
related:
  - title: "Functions | MotherDuck Docs"
    url: "https://motherduck.com/docs/sql-reference/duckdb-sql-reference/functions/"
  - title: "DuckLake Architecture Deep Dive"
    url: "https://motherduck.com/blog/ducklake-architecture-deep-dive/"
  - title: "Running dual execution (or hybrid) queries | MotherDuck Docs"
    url: "https://motherduck.com/docs/key-tasks/running-hybrid-queries/"
---

# GENERATE_SERIES

> GENERATE_SERIES(start, stop, step) produces a sequence of numeric, date, or timestamp values, commonly used to build calendars or fill gaps in time-series data.

## Overview
`GENERATE_SERIES` produces an evenly spaced sequence of values between a start and stop bound, inclusive of both ends. It's most often used to generate a synthetic date/number spine — for example, a complete calendar of days — that you then `LEFT JOIN` real data onto, so that days or periods with zero activity still appear in the output.

```sql
SELECT day
FROM generate_series(DATE '2026-01-01', DATE '2026-01-31', INTERVAL '1 day') AS t(day);
```

```sql
-- Fill gaps: every day in January, even ones with no orders
SELECT d.day, COALESCE(SUM(o.revenue), 0) AS revenue
FROM generate_series(DATE '2026-01-01', DATE '2026-01-31', INTERVAL '1 day') AS d(day)
LEFT JOIN orders o ON DATE_TRUNC('day', o.order_date) = d.day
GROUP BY ALL
ORDER BY ALL;
```

## DuckDB specifics
DuckDB's `generate_series(start, stop, step)` is dual-purpose: used in a `FROM` clause, it behaves as a table function producing one row per value (as in the examples above); used inside a `SELECT` list, it instead returns a single `LIST` value containing the whole sequence. DuckDB's `generate_series` is inclusive of the stop value, whereas its sibling function `range(start, stop, step)` behaves the same way but excludes the stop value — mirroring Python's `range()`. Both work over integers, dates, and timestamps (with an `INTERVAL` step for date/timestamp sequences):

```sql
SELECT generate_series(1, 5) AS nums;
-- [1, 2, 3, 4, 5]  (a single LIST, since it's in the SELECT list)

SELECT * FROM generate_series(1, 5) AS t(n);
-- 5 rows: 1, 2, 3, 4, 5
```