最大何人まで座れますか?
次に取り上げるのは、前問の裏返しの問題です。すなわち、「現在の空席状況だと、最大で何人が連続で座れるか」を調べる、というものです。換言するなら最大シーケンスを求める問題です。次のような「Seats3」テーブルを使います。
| seat(座席) | status(状態) |
| 1 | 占 |
| 2 | 空 |
| 3 | 空 |
| 4 | 空 |
| 5 | 空 |
| 6 | 占 |
| 7 | 空 |
| 8 | 占 |
| 9 | 空 |
| 10 | 空 |
このサンプル・データだと、大きさ4の「2~5」のシーケンスが答えになります。この問題を解くには、最初にシーケンスをすべてリストアップするビューを作るのが簡単です。そうすれば、後はそのビューから最大のシーケンスを選択するだけです。
「Seats3」テーブルにおいて、ある席番号Aから別の席番号Bまでがシーケンスである、ということを保証するには、以下の三つの条件をすべて満たすことです。
- 条件1.始点―終点間のすべての座席の状態が「空」である
- 条件2.始点の1つ前の座席の状態が「空」ではない
- 条件3.終点の1つ後の座席の状態が「空」ではない
例えば、条件1についてみると、次のように途中に「占」の席があったりしたら、その期間がシーケンス失格なことは明らかです。

同様に、始点と終点をもっと伸ばせるような期間も、やはり選択対象からは除外します(その伸ばした始点と終点を持つ期間があれば十分ですから)。


この問題でも、前問と同様にステップ1とステップ2の手順を踏むことで、次のようなビューが作れます。
CREATE VIEW Sequences (start_seat, end_seat, seat_cnt) AS SELECT S1.seat AS start_seat, S2.seat AS end_seat, S2.seat - S1.seat + 1 AS seat_cnt FROM Seats3 S1, Seats3 S2 WHERE S1.seat <= S2.seat --ステップ1:始点と終点の組み合わせを作る AND NOT EXISTS --ステップ2:シーケンス内のすべての行が満たすべき条件を記述する (SELECT * FROM Seats3 S3 WHERE ( S3.seat BETWEEN S1.seat AND S2.seat AND S3.status <> '空') --条件1の否定 OR (S3.seat = S2.seat + 1 AND S3.status = '空' ) --条件2の否定 OR (S3.seat = S1.seat - 1 AND S3.status = '空' )); --条件3の否定
このビューは、次のような内容を含みます。
start_seat end_seat seat_cnt
------------ ---------- ----------
2 5 4
7 7 1
9 10 2
大きさが1の「7~7」をシーケンス(連番)と呼ぶのは語義矛盾の気もしますが、今回は一応含めています(もし大きさ1の集合を除外したければ、WHERE句の「S1.seat <= S2.seat」の等号を除いてください)。
ここまで来れば、後は簡単です。このビューから、席数(seat_cnt)が最大の行を選択しましょう。
SELECT start_seat, '~', end_seat, seat_cnt FROM Sequences WHERE seat_cnt = (SELECT MAX(seat_cnt) FROM Sequences);
この問題においても、まずはステップ1に従い、自己結合(S1.seat <= S2.seat)で、始点と終点の組み合わせを作っています。これは、ランキングや累計を求めるクエリと同じ非等値結合です。
そして始点と終点が決まったら、今度はその内部のすべての行が満たすべき条件を記述するために、始点と終点の間を移動するS3を追加することや、全称文を存在文の否定に同値変形することも、前問とまったく同じです(今回はさらに、S3に両端の1つ外側の点も代表させていますが)。
この2つのステップが、SQLで順序集合を扱うときの定石です。次の最終問題も、この2点を意識して考えてください。今度は、発展版として「第四の集合」まで使います。
