私はおそらく、この基本的な考え方に似た「ブルートフォース」構造を採用するでしょう。

(実際のモデルにはさらに多くのフィールドが存在する必要があります。これは、テーブル間の関係を確立するために必要な最低限の要素を含む単純化されたバージョンにすぎません。)
チケットの「カバー」はTICKET_STOPテーブルを介して停止します。たとえば、チケットが3つのストップをカバーする場合、TICKET_STOPにはそのチケットに関連する3つの行が含まれます。そのチケットでカバーされていない他の2つの停車地がある場合、そこに関連する行はありませんが、別のチケットがこれらの停車地をカバーすることを妨げるものは何もありません。
自由な使用法または自然キー/識別関係により、2つのチケットが同じ座席/停車地の組み合わせをカバーできないことが保証されます。TICKET_STOPテーブルで、LINE.LINE_IDがひし形の依存関係の両端に沿ってどのように「移行」し、下部でのみマージされるかを確認します。
このモデル自体では、単一のチケットが一部の停車地を「スキップ」するなどの異常から保護することはできません。アプリケーションロジックを介していくつかのルールを適用する必要があります。ただし、次のように、旅行のどの部分でどの座席が空いているかをかなり簡単かつ迅速に判断できるようにする必要があります。
SELECT *
FROM
STOP CROSS JOIN SEAT
WHERE
STOP.LINE_ID = :line_id
AND SEAT.BUS_NO = :bus_no
AND NOT EXIST (
SELECT *
FROM TICKET_STOP
WHERE
TICKET_STOP.LINE_ID = :line_id
AND TICKET_STOP.BUS_ID = :bus_no
AND TICKET_STOP.TRIP_NO = :trip_no
AND TICKET_STOP.SEAT_NO = SEAT.SEAT_NO
AND TICKET_STOP.STOP_NO = STOP.STOP_NO
)
(パラメータプレフィックス:
をDBMSに適したものに置き換えます。)
このクエリは基本的に、特定の路線とバスの停車地と座席のすべての組み合わせを生成し、特定の旅行でチケットによってすでに「カバー」されているものを破棄します。「カバーされていない」ままのそれらの組み合わせは、その旅行のために無料です。
次の項目を簡単に追加できます。STOP.STOP_NO IN ( ... )
または句に追加SEAT.SEAT_NO IN ( ... )
してWHERE
、特定の停車地または座席での検索を制限します。