数値系列と日付系列を生成する
再帰を使って、欠損補完やカレンダー用の系列を生成します。
「数値系列と日付系列を生成する」はCoddyKit上の無料Coding Interview Prepレッスンです。 これはレッスン3/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはCoding Interview Prep学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Coding Interview Prepコースには全4レッスンが含まれています。
階層のない再帰
再帰CTEは、ツリー構造だけに使うものではありません。もう1つの主要な用途が系列の生成です。数値の並びや、ある範囲内のすべての日付を生成できます。面接では、どのテーブルにも存在しない行を生成するギャップ埋めが必要な問題で、この用途が問われます。
典型的な問題は、「その月の日別売上を、売上がゼロの日も含めて表示してください」です。まずすべての日付を生成しなければ、存在しない日を表示することはできません。
単純な数値系列
アンカーで最初の数値を設定し、再帰メンバーが反復ごとに1を加え、再帰メンバー内のWHEREで停止します。これにより1から10までを生成します。
WITH RECURSIVE nums AS (
SELECT 1 AS n
UNION ALL
SELECT n + 1 FROM nums WHERE n < 10
)
SELECT n FROM nums;終了条件
組織図とは異なり、数値系列には停止すべき自然な葉ノードがありません。そのため、無限に増やせてしまいます。そこで、再帰メンバーに明示的な停止条件としてWHERE n < 10を追加する必要があります。
nが10に達すると、次の反復ではWHEREによって唯一の候補行が除外され、再帰メンバーは何も返さなくなり、再帰が停止します。このガードを忘れることが、面接で無限に続く再帰を引き起こす最大の原因です。
範囲をパラメーター化する
値や変数から上限を与えることで、柔軟な系列にできます。ここでは、指定されたNを上限として1からNまでを生成します。同じ形で0始まりの系列や、一定間隔の系列も生成できます。アンカーと増分を変更するだけです。
WITH RECURSIVE nums AS (
SELECT 1 AS n
UNION ALL
SELECT n + 2 FROM nums WHERE n + 2 <= 99
)
SELECT n FROM nums; -- odd numbers 1,3,5,...,99日付系列の生成
整数の計算を日付の計算に置き換えると、カレンダーを生成できます。アンカーが開始日を表し、再帰メンバーが終了日を超えるまで1日ずつ加算します。
1日を加算する構文は方言によって異なります。ここでは、intervalを使うPostgres形式を示しています。
WITH RECURSIVE cal AS (
SELECT DATE '2024-01-01' AS d
UNION ALL
SELECT d + INTERVAL '1 day'
FROM cal
WHERE d < DATE '2024-01-31'
)
SELECT d FROM cal;LEFT JOINによるギャップ埋め
次に、カレンダーと実データを組み合わせます。すべての日付を生成してから、売上テーブルをLEFT JOINします。存在しない日の値はNULLになるので、COALESCEで0に変換します。
この「基準系列を生成し、ファクトを左結合する」という2段階のパターンが、ギャップ埋めの解答の基本です。
WITH RECURSIVE cal AS (
SELECT DATE '2024-01-01' AS d
UNION ALL
SELECT d + INTERVAL '1 day' FROM cal
WHERE d < DATE '2024-01-07'
)
SELECT cal.d, COALESCE(SUM(s.amount), 0) AS total
FROM cal
LEFT JOIN sales s ON s.sale_date = cal.d
GROUP BY cal.d
ORDER BY cal.d;月次・週次の基準系列
増分を変更すれば、より大きな単位のカレンダーを作成できます。月単位の基準系列にはINTERVAL '1 month'を、週単位にはINTERVAL '7 day'を追加します。空の月も含めた月次レポートを求められたときに便利です。
WITH RECURSIVE months AS (
SELECT DATE '2024-01-01' AS m
UNION ALL
SELECT m + INTERVAL '1 month' FROM months
WHERE m < DATE '2024-12-01'
)
SELECT m FROM months;方言ごとの日付計算の違い
日付の算術は、これらのクエリの中で最も移植性が低い部分です。次の違いを把握しておきましょう。
- Postgres:
d + INTERVAL '1 day'。 - MySQL:
DATE_ADD(d, INTERVAL 1 DAY)。 - SQL Server:
DATEADD(DAY, 1, d)。 - SQLite:
date(d, '+1 day')。
再帰の構造は同じで、日付関数だけが変わると説明できれば、方言の違いを理解した強い回答になります。
再帰とgenerate_seriesの比較
Postgresには、再帰を使わずに数値や日付を生成できる組み込みのgenerate_series()があります。こちらのほうが高速で、読みやすい書き方です。
SELECT generate_series(DATE '2024-01-01', DATE '2024-01-31', INTERVAL '1 day');
面接で使うデータベースが対応しているなら、こちらを優先してください。ただし、多くのエンジン(MySQLや、最近のバージョンより前のSQL Server)にはこの機能がありません。その場合に、再帰CTEが移植性の高い代替手段になります。
再帰の上限に注意
大きな系列を生成すると、データベースエンジンの再帰上限に達することがあります。SQL ServerのデフォルトはMAXRECURSION 100なので、OPTION (MAXRECURSION 0)を追加して上限を解除しない限り、365日分のカレンダーは失敗します。
Postgresには固定の上限はありませんが、終了条件を誤った系列は、メモリを使い果たすまで実行される可能性があります。規模を拡大する前に、終了条件が正しいことを必ず確認してください。
-- SQL Server: lift the 100-row recursion cap
-- ...recursive CTE here...
SELECT * FROM cal
OPTION (MAXRECURSION 0);系列をCROSS JOINする
生成した系列は、単独で最終結果になるとは限りません。数値CTEを用意したら、それをCROSS JOINして行を展開できます。たとえば、各注文行を数量の回数だけ繰り返したり、顧客ごとに日付範囲を展開したりできます。
再帰は最終的な答えではなく、再利用できる構成要素を生成するものだと理解していることが、暗記しただけの回答と洗練された面接回答を分けます。
WITH RECURSIVE nums AS (
SELECT 1 AS n
UNION ALL
SELECT n + 1 FROM nums WHERE n < 10
)
SELECT o.order_id, nums.n AS unit
FROM orders o
JOIN nums ON nums.n <= o.quantity;確認問題
数値系列や日付系列で、停止条件が重要なのはなぜですか?
まとめ
再帰を使えば、どのテーブルにも存在しない行を生成できます。
- アンカーで最初の値を設定し、再帰メンバーで値を増分します。
- 系列には自然な終端がないため、必ず明示的な終了条件を追加します。
- 日付または数値の基準系列を作り、ファクトを
LEFT JOINして、ギャップを埋めるためにCOALESCEを使います。 - 利用できる場合は
generate_seriesを優先し、SQL ServerではMAXRECURSIONに注意します。
次は、再帰が暴走するのを防ぐ安全対策です。
よくある質問
「数値系列と日付系列を生成する」レッスンは無料ですか?
はい。「数値系列と日付系列を生成する」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Coding Interview Prepコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Coding Interview Prepコースには全4レッスンが含まれています。
「数値系列と日付系列を生成する」で何を学びますか?
再帰を使って、欠損補完やカレンダー用の系列を生成します。 ブラウザで直接実行するハンズオンコードでCoding Interview Prepを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
Coding Interview Prepを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのCoding Interview Prepは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/4です。
「数値系列と日付系列を生成する」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このCoding Interview Prepレッスンでコードを書いて実行できますか?
はい。すべてのCoding Interview Prepレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。