ノーコードの予約管理で二重予約を防ぐ設計:枠・状態・締切の決め方
AIを使いこなすClaudeで執筆し、GPTで自動校閲・修正しています。

結論から言うと、二重予約を防ぐ鍵は画面の工夫より、「1つの枠を1行で持ち、予約はその枠IDを参照し、空き確認から枠の確保までを他の予約処理が割り込めない形で行う」というデータと処理の設計です。「満席」と表示するボタンを置いても、2人が同時に押せば両方通ることがあります。表示は案内にすぎず、重複を止めるのはデータ側の仕組みです。 この記事では、予約枠表と予約表の列の例、状態名と締切ルールの決め方、確定時の確認手順を示します。表の例はすべて「説明用の例」で、特定のツールで動作を確かめた結果ではありません。
二重予約が起きるデータの形
起きやすいのは次の3パターンです。
- 予約表だけで管理している:「5月10日 14:00」という日時が予約ごとに文字で書かれ、「その枠が空いているか」を調べる元の表がない。
- 空き状況を計算で出している:予約件数を数えて表示するだけなので、2人が同時に確定すると両方とも「空き」と判断する。
- キャンセルや期限切れの扱いが曖昧:キャンセルした行が残り、枠を埋めたままに見える。または逆に、仮押さえを数えず、空きがあると判断してしまう。
Bubbleの公式マニュアルによると、「Do a search for」を使った要素は、ワークフローで初期取得した検索を除き、他のユーザーによるデータ変更も画面に反映されます(Finding data)。ただしこれは画面表示の話です。私が読んだページには、2人が同時に書き込んだときの排他制御の説明はありませんでした。「画面が最新になる」ことと「同時に確定しても重複しない」ことは別の問題として扱ってください。
予約枠表と予約表の列例
予約枠表(1行=1つの予約可能な枠)
- 枠ID、内容:変更しない一意の値、例(説明用):S-0001
- 開始日時、内容:日付と時刻を1つの日時型で、例(説明用):2026-11-10 14:00
- 終了日時、内容:同上、例(説明用):2026-11-10 14:30
- 担当・場所、内容:同じ時間でも担当ごとに枠を分ける、例(説明用):相談室A
- 定員、内容:1枠で受けられる数、例(説明用):1
- 占有人数、内容:「確定」と期限内の「仮押さえ」の人数合計(予約表から集計)、例(説明用):0
- 枠状態、内容:受付中/満席/休止、例(説明用):受付中
- 受付締切、内容:予約を受ける最終日時、例(説明用):2026-11-09 18:00
ポイントは「担当・場所ごとに枠を別の行にする」ことです。「14:00に1件」ではなく「相談室Aの14:00」と「相談室Bの14:00」を別の枠にすると、何が重複かが明確になります。ただし、同じ担当者や場所を複数の枠で共有する場合は、その資源の時間帯が重ならないかも確認します。
予約表(1行=1件の予約)
- 予約ID、内容:変更しない一意の値、例(説明用):R-0001
- 枠、内容:予約枠表の枠IDを参照(手入力しない)、例(説明用):S-0001
- 予約者名・連絡先、内容:試験時は説明用のダミーで試す、例(説明用):山田花子(ダミー)
- 人数、内容:定員を超えないか判定に使う、例(説明用):1
- 予約状態、内容:後述の状態名、例(説明用):仮押さえ
- 作成日時、内容:自動入力、例(説明用):2026-11-01 10:00
- 仮押さえ期限、内容:仮押さえのときだけ値を入れる、例(説明用):2026-11-01 10:15
- 有効枠キー、内容:状態が「仮押さえ」「確定」のときだけ枠IDを入れ、それ以外は空、例(説明用):S-0001
「有効枠キー」は定員1の枠の重複検出に使う補助列です。空欄を除き、同じ値が2行にあれば重複の候補ですが、列を作るだけでは同時書き込みを防げません。期限切れへの更新が遅れた仮押さえは除いて判定します。定員が複数ある枠では、「確定」と期限内の「仮押さえ」の人数合計が定員を超えていないかで判定します。
IDを手入力や日時の文字列にしないことも重要です。AppSheetのヘルプでは、キーは行を識別するもので、後から変えると参照関係に影響するため最初に慎重に決めるよう説明されています(Editable keys)。どのツールでも、IDは自動生成の値にして変更しない運用が安全です。
状態名の決め方
状態は、「枠を占有しているか」で分けると迷いません。
- 仮押さえ、枠を占有する:期限内だけする、次に進める状態:確定/期限切れ/キャンセル、変わるきっかけ:枠の確保成功で入り、確定操作・期限超過・取消操作で変わる
- 確定、枠を占有する:する、次に進める状態:キャンセル/完了/無断欠席、変わるきっかけ:利用者の確定操作
- キャンセル、枠を占有する:しない、次に進める状態:なし、変わるきっかけ:利用者または運営の操作
- 期限切れ、枠を占有する:しない、次に進める状態:なし、変わるきっかけ:仮押さえ期限の超過
- 完了、枠を占有する:しない(過去分)、次に進める状態:なし、変わるきっかけ:利用終了後の運営の記録
- 無断欠席、枠を占有する:しない(過去分)、次に進める状態:なし、変わるきっかけ:利用時間終了後の運営の記録
決め方のルールは3つです。
- 占有する状態を最小にする:占有は「仮押さえ」と「確定」の2つだけにし、重複判定の対象をはっきりさせます。
- 状態名は動作ではなく結果で付ける:「キャンセルする」ではなく「キャンセル」。画面の文言と一致させると取り違えが減ります。
- 行は消さず状態を変える:削除すると経緯が追えず、問い合わせに答えられません。
締切ルールの決め方
締切は「誰が・いつまで・何をできるか」を1つずつ決めます。数値は例で、運営の事情に合わせて変えてください。
- 受付締切、決めること:開始の何時間前まで新規予約を受けるか、例(説明用):前日18:00
- 仮押さえ期限、決めること:入力中の確保を何分保つか、例(説明用):15分
- 変更・キャンセル締切、決めること:利用者が自分で操作できる期限、例(説明用):開始24時間前
- 当日の扱い、決めること:締切後の連絡手段、例(説明用):電話のみ
- 時刻の基準、決めること:保存と表示のタイムゾーン、例(説明用):日本時間で統一
仮押さえ期限が過ぎた行は、予約表の「期限切れ」に変えて有効枠キーを空にします。これを定期処理に任せるなら、処理が止まったときに枠が埋まったままになる点を確認しておいてください。安全側の工夫として、枠の空き判定では「仮押さえ期限が現在より後の行だけ」を数える方法もあります。
確定時の確認手順
確定ボタンの内部処理は、次の順にします。手順1〜3はサーバー側で、他の予約処理が割り込めない一体の処理にする必要があります。検索し直すだけでは、この条件を満たしません。
- 新規の確保なら、枠が休止中でなく受付締切前かを確認する。仮押さえからの確定なら、自分の予約で期限内かも確認する。満席表示でも、自分の仮押さえ分の確定は区別して扱う。
- 自分の予約を除き、その枠の「確定」と期限内の「仮押さえ」の人数をそのタイミングで検索し直して合計する。その合計に今回の人数を足しても定員以下かを確認する。
- 条件を満たせば、自分の既存の仮押さえ行を「確定」に更新する。新規予約なら1行だけ作成し、有効枠キーを入れる。同じ予約IDへの再送は重複作成しないようにする。
- 保存成功後に確定を通知する。保存後の再集計で超過を検出した場合は運営へ知らせ、事前に決めた優先順で調整する。
仮押さえを作るときも、空き確認から保存までを同じ仕組みで保護します。確定後の再確認や重複一覧は異常の検出用で、二重予約の防止を保証するものではありません。定員1では空欄を除く有効枠キーの重複、定員が複数なら占有人数の超過を一覧にします。
GoogleスプレッドシートとApps Scriptを使う場合は、LockServiceで同じスクリプト内の対象処理を1つずつ実行できます。スクリプトロックは、利用者が違っても保護した区間を同時に実行させない仕組みで、waitLockやtryLockで取得を試みます(LockService)。取得成功後に空き確認から保存までを行い、取得できなければ予約を確定しません。スプレッドシートへの書き込みはSpreadsheetApp.flush()で反映してからロックを解放します(Lock)。直接編集や別スクリプトからの更新はこのロックでは止まらないため、枠を確保・変更する経路を統一します。これはコードを使う構成です。
AppSheetの公式ヘルプでは、変更単位は1行で、同じ行への競合更新は後の書き込みが残ると説明されています(Concurrent usage with multiple users)。これは、複数の予約行の空き確認と保存を一体で行う保証ではありません。使うツールと接続先でこの保証を確認できない場合は、自動確定を始めず、利用者からは受付依頼だけを受け、担当者が1件ずつ確認・確定する運用にします。本番前には、次の試験も行います。
- 別々のブラウザ(またはシークレットウィンドウ)で、同じ枠の確定ボタンをほぼ同時に押す。
- 保存先で、同じ枠の占有人数が定員を超えていないかを見る。
- 仮押さえからの確定、期限切れ直後の確定、同じ予約の再送も試す。
- 超過が出た場合は自動確定を止め、原因を修正する。試験に通ることだけで、あらゆる同時実行への保証が得られるわけではありません。
AIに任せる範囲と依頼文
AIには、列の設計案と抜け漏れの洗い出しを任せられます。ただし、実際のツールの設定画面で通るか、同時に押したときの結果はあなたが確かめる必要があります。
次の依頼文の【 】を自分の条件に置き換えて使えます。
ノーコードで【Bubble/Glide/AppSheet/スプレッドシート】を使い、
【相談予約】の予約アプリを作ります。プログラミングは不慣れです。
条件:
- 枠は【30分単位】、担当は【2名】、定員は枠ごとに【1】
- 受付締切は【前日18:00】、仮押さえは【15分】
- キャンセルは【開始24時間前まで】
次を表形式で出してください。
1. 予約枠表と予約表の列(列名、型、入力方法)
2. 予約状態の一覧と、状態ごとに枠を占有するか
3. 二重予約が起きうる場面を5つ、それぞれの防ぎ方
4. 確定ボタンの処理手順(確認の順番)
最後に、私が実際の画面で確かめるべき項目を挙げてください。個人情報は入力しません。氏名や連絡先は、試験用にダミー値へ置き換えてください。返ってきた列名が、使うツールの型(日時、参照、選択肢など)で作れるかは、設定画面で一つずつ確認します。
公開前チェックリスト
- 枠は1行1枠で、担当・場所ごとに分けたか
- 予約の枠は参照で選ばせ、手入力にしていないか
- 占有する状態は「仮押さえ」「確定」だけか
- 受付締切・仮押さえ期限・キャンセル締切を数値で決めたか
- 仮押さえ・確定時の空き確認から保存までを、他の予約処理が割り込めない形で行えるか。保証を確認できなければ担当者が1件ずつ確定する運用にしたか
- 重複を一覧で見られるビューがあるか
- 同時に押す試験を自分でやったか

