2-1関係演算
表に作り直してから1か月がたった月曜の夕方、店長がレジの横で予約の画面をのぞき込みました。「先月いちばん予約が多かったパンはどれだろう。それから、3回以上予約してくれた常連さんには、お礼のはがきを出したいんだよね」。紙の台帳のころなら、ページをめくって正の字を書き、1日がかりで数えていた仕事です。いまは、9月の予約が312行の表に並んでいます。答えはこの中にあるはずでした。ところが、ミナトが画面を上から下までスクロールしても、どの行をどう数えればいいのかが見えてきません。相棒に打ち込みます。「表はあるのに、答えの取り出し方が分からない」
相棒の返事は、表を1枚の紙のように眺めるのをやめよう、というものでした。「答えは表の中にあるけれど、そのままの形では出てこない。必要な行だけ、必要な列だけを切り出して、ほかの表とつなぐ。そうやって新しい表を作ると、答えがその形で出てくるよ」。
こむぎ堂の表は、第1章で分けた3つです。予約の表には予約番号・お客さま番号・商品・個数・受取日が、お客さまの表にはお客さま番号・名前・住所・電話番号が入っています。9月の予約の表から6行だけを抜き出すと、次のようになります。この章の計算は、この6行で確かめていきます。
| 予約番号 | お客さま番号 | 商品 | 個数 | 受取日 |
|---|---|---|---|---|
| R01 | C1 | 食パン | 2 | 9月6日 |
| R02 | C2 | クロワッサン | 3 | 9月6日 |
| R03 | C1 | 食パン | 1 | 9月13日 |
| R04 | C3 | 食パン | 2 | 9月13日 |
| R05 | C1 | あんパン | 1 | 9月20日 |
| R06 | C2 | 食パン | 1 | 9月27日 |
表から欲しい答えを取り出す操作は、3つの動きの組み合わせでできています。一つめは、条件に合う行だけを取り出す選択です。「商品が食パンの行」を選べば、6行のうち R01・R03・R04・R06 の4行が残ります。表を横に切る操作です。二つめは、必要な列だけを取り出す操作で、こちらは表を縦に切ります。予約の表から「お客さま番号」と「商品」の2列だけを残せば、誰が何を予約したかだけが見える表になります。
三つめが、表どうしをつなぐ操作です。予約の表には名前が無く、お客さま番号しかありません。はがきの宛名を書くには、お客さまの表と組み合わせる必要があります。共通の列(ここではお客さま番号)を手がかりに、複数の表の行を対応づけて1つの結果として取り出すことをデータの結合、または表の結合といいます。R01 のお客さま番号 C1 をお客さまの表で探せば、「佐藤」の名前と住所が同じ行に並びます。
ここから基本情報!演算としての表の操作
表を入力にして表を出力する操作を、数学の演算のように体系立てたものを関係代数といいます。関係データベースの理論の基礎で、SQLの文もこの考え方に基づいています。関係代数で使う一つ一つの操作、たとえば選択や結合を関係演算と呼びます。
関係演算のうち、必要な列だけを取り出す演算が射影です。予約の表を「お客さま番号」「商品」の2列に射影すると、2列の表ができます。実際の問合せでは、行を選んでから列を絞るという使い方がほとんどで、この組み合わせを選択と射影と呼びます。「食パンを予約したお客さま番号の一覧」なら、商品が食パンの行を選択し、お客さま番号の列に射影します。関係代数では同じ行を1つにまとめるので、答えは C1・C3・C2 の3行です(SQLでは、重複を除く指定をしない限り C1 が2回出て4行になります)。
結合にはいくつかの形があります。2つの表で名前の同じ列(お客さま番号)の値が等しい行どうしをつなぎ、その列を1本にまとめて残すのが自然結合です。予約の表とお客さまの表を自然結合すると、お客さま番号の列は1本だけで、その右に名前・住所・電話番号が付いた表ができます。
一方、条件を何も付けずに、片方の表の全行ともう片方の表の全行を総当たりで組み合わせる演算が直積です。3行の表と2行の表の直積は 3×2=6行で、列の数は両方の合計になります。直積をそのまま使う場面は少ないのですが、結合は「直積を作ってから、共通の列の値が等しい行を選択する」と言い直せます。自然結合なら、そのあと同じ列を1本にまとめる射影も加わります。
もう一つ、商という演算があります。予約の表の「お客さま番号・商品」の2列を、「食パン」「クロワッサン」の2行の表で割ると、その両方を予約したお客さま番号だけが残ります。上の6行では、食パン(R06)とクロワッサン(R02)の両方を予約した C2 です。C1 は食パンとあんパンなので、クロワッサンが欠けて外れます。「〜をすべて満たすもの」を探すときの演算です。
基本情報の範囲ここまで
行を選び、列を絞り、表をつなぐ。9月の予約を調べる問いの多くは、この組み合わせで書けます。ところが、宛名を集め始めたミナトは、もう一つの問題に気づきました。こむぎ堂には、店頭で予約を受けたお客さまの名簿と、ネット予約に登録したお客さまの名簿が、別々の表として残っていたのです。店頭の名簿には C1・C2・C3 の3人、ネットの名簿には C2・C4 の2人がいます。
列の並びが同じ2つの表を1つにまとめる操作を、併合と呼ぶことがあります。ただし、併合の意味は資料によって少しずつ違います。2つの表の行を合わせる「和」と同じ意味で使う資料もあれば、並び順をそろえた2つのファイルを1本にまとめる処理を指す資料もあります。どちらでも、2つを1つにまとめるという点は変わりません。はがきの宛先は、2つの名簿をまとめて、両方に出てくる C2 を1人と数えれば4人です。
ここから基本情報!集合演算
列の並びが同じ2つの表は、集合として計算できます。どちらかの表にある行をすべて集めるのが和、両方にある行だけを残すのが積(共通)、一方にあって他方に無い行を残すのが差です。SQLでは、それぞれ次の演算子で書きます。
| 演算 | SQLの演算子 | 店頭とネットの名簿での結果 |
|---|---|---|
| 和 | UNION | C1・C2・C3・C4(4行) |
| 積 | INTERSECT | C2(1行) |
| 差(店頭からネットを引く) | EXCEPT | C1・C3(2行) |
UNION は、何も指定しなければ同じ行を1つにまとめるので、C2 は1回しか出てきません。重複を残したいときは UNION ALL と書きます。差は引く順で答えが変わり、ネットの名簿から店頭の名簿を引けば C4 だけが残ります。製品によっては、EXCEPT の代わりに MINUS という名前の演算子を使います。
基本情報の範囲ここまで
2-2データベース言語の種類
翌日、ミナトはデータベースの管理画面を開き、文字を打ち込む欄に「食パンの予約だけ出して」と書いてみました。画面には文法の誤りを知らせる赤い文字が出ただけです。そこへ店長が別の注文を持ってきます。「予約を受けるたびに表へ直接書き込むのは怖いから、紙の予約票みたいな画面がほしいな」。
データベースに対して、必要なデータを探して取り出すよう求めることを問合せ(クエリ)といいます。問合せは日本語ではなく、データベースのための言語で書きます。関係データベースで広く使われているのがSQLで、多くのデータベース管理システムがこの言語で指示を受け付けます。前の節の行の選択や表の結合も、SQLの文として書けば、データベースが結果の表を返します。
店長の注文した画面のように、紙の伝票に近い見た目で、データの入力・参照・更新ができるようにした画面をフォームといいます。お客さま番号、商品、個数、受取日の欄を埋めて「登録」を押すと、裏で予約の表に1行が足されます。使う人はSQLを知らなくても、表の決まりどおりにデータを書き込めます。
ここから基本情報!SQLの文の種類と使い方
SQLの文は、役割で大きく二つに分かれます。表やビューの形、値の決まり、権限を定める文の集まりがDDL(Data Definition Language、データ定義言語)で、表を作る CREATE、形を変える ALTER、表を消す DROP などがあります。表に入ったデータを取り出したり書き換えたりする文の集まりがDML(Data Manipulation Language、データ操作言語)で、SELECT・INSERT・UPDATE・DELETE がこれに当たります。こむぎ堂でいえば、予約の表を作ったのが DDL、毎日の予約を書き込み、数えるのが DML です。
SQLの使い方にも二通りあります。SQLの文をそれだけで単独に使う独立言語方式と、ほかのプログラム言語で書いたプログラムの中から使う親言語方式です。ミナトが管理画面の欄にSQLの文を打ち込み、その場で結果を確かめるのは独立言語方式で、このように端末から対話的にSQLを入力して実行する使い方を会話型SQLといいます。
シラバスには、利用の形としてコマンド方式も挙がっています。利用者が命令の文(コマンド)を直接入力してデータベースを操作するやり方を指す語と考えられますが、シラバスは名前を挙げているだけで、会話型SQLとの違いまでは示していません。メニューを選んだりフォームに書き込んだりして操作するやり方と対になる語、と押さえておけば十分です。
親言語方式は、ネット予約のプログラムが中から表を読み書きするときに使います。その方法の一つがモジュール言語で、SQLの文を手続きの単位(モジュール)にまとめて別に用意しておき、プログラムからはそのモジュールを呼び出して実行します。プログラムの側にはSQLの文が現れず、呼出しの命令だけが残ります。プログラムの中にSQLの文をじかに書き込む方法は、2-6で扱います。
基本情報の範囲ここまで
2-3データ定義言語
週末だけ入るアルバイトのために、レジの画面を作ることになりました。受け取りに来たお客さまの予約を探せれば十分で、住所や電話番号まで見せる必要はありません。かといって、お客さまの表から電話番号の列を消すわけにもいきません。予約の確認の電話には、その番号が要るからです。
元の表には手を付けず、見せる部分だけを切り出した表を用意する方法があります。ビューは、元の表から選んだ列や行、あるいは複数の表を結合した結果を、1つの表のように見せる仮想的な表です。お客さまの表からお客さま番号と名前の2列だけを取り出した「レジ用」のビューを作れば、レジの画面にはその2列しか出てきません。ビューはデータを別に持たず、使われるたびに元の表から中身を作ります。名前の書き間違いを元の表で直せば、レジ用のビューにもすぐに反映されます。
見える範囲を絞ったら、誰がどの表やビューを使えるかも決めます。表やビューに対して、読む・書き込む・消すといった操作を誰に許すかを定めた権限をアクセス権といいます。アルバイトの利用者にはレジ用のビューを読むアクセス権だけを与え、お客さまの表そのものには触れられないようにします。ビューとアクセス権を組み合わせると、同じデータベースを、人によって違う範囲だけ見せられます。
ここから基本情報!表の形と決まりを定める
ビューと対になるのが、データが実際に格納されている表で、実表または基底表と呼びます。お客さまの表も予約の表も実表です。実表に関係演算を施して導き出される、データとしては格納されていない表をまとめて導出表といい、ビューや問合せの結果の表がこれに当たります。
ビューの中には、ビューを通してデータを書き換えられるものと、書き換えられないものがあります。1つの実表から列や行を取り出しただけのビューなら、ビューの1行が実表のどの行に当たるかが決まるので、更新できます。予約を商品ごとに集計したビューは事情が違います。「食パン 4件」の1行は実表の4行をまとめたものなので、その数字を書き換えても、どの行をどう直せばよいかが決まりません。こうしたビューは更新できません。
実表を作る DDL の文が CREATE TABLE です。列ごとに、入れる値の種類(データ型)と、値が守るべき決まり(制約)を書きます。こむぎ堂の予約の表なら、次のように定めます。
| 列 | データ型 | 制約 |
|---|---|---|
| 予約番号 | CHAR(3) | 主キー |
| お客さま番号 | CHAR(2) | 外部キー(お客さまの表を参照) |
| 商品 | VARCHAR(20) | NOT NULL |
| 個数 | INTEGER | NOT NULL、CHECK (個数 > 0) |
| 受取日 | DATE | NOT NULL |
データ型のうち、文字の並びを入れるのが文字型で、長さの決まった CHAR や、長さが変わる VARCHAR があります。整数の INTEGER や、小数点以下の桁数を決めて持つ DECIMAL のように数値を入れる型が数値型、年月日を入れる DATE などが日付型です。受取日を文字型にしてしまうと「9/6」と「9月6日」の2通りの書き方が入り込み、日付の順に並べることも、2つの日付の間が何日あるかを求めることも、正しくできなくなります。
制約は、表に間違ったデータが入るのを防ぐ決まりです。列の値が指定した条件を満たすときだけ格納を許すのが検査制約(CHECK 制約)で、個数に CHECK (個数 > 0) を付ければ、0やマイナスの個数の予約は登録しようとした時点で拒まれます。列に値の無い状態(NULL)を許さないのが非NULL制約(NOT NULL 制約)で、商品の欄が空のままの予約は登録できません。主キーの列には、指定しなくても非NULLの決まりがかかります。第1章の、主キーの値の重複を許さない一意性制約や、外部キーの参照制約も、同じように CREATE TABLE の中で定めます。
基本情報の範囲ここまで
2-4SELECT文
いよいよ店長の問いに答える番です。ミナトが最初に試したのは、9月の予約のうち「食パン」の付く商品だけを出すことでした。こむぎ堂の食パンは「食パン」「食パン(山型)」「食パン(ハーフ)」の3種類に分かれていて、商品名が「食パン」と完全に一致する行を探すだけでは、山型とハーフが漏れてしまいます。
文字の一部が決まっていないものを探すときは、決まっていない部分を特別な記号で表します。検索の条件の中で、未確定の文字や文字列の代わりに使う記号をワイルドカードといいます。表計算ソフトやファイルの検索では、「*」が0文字以上の任意の文字列、「?」が任意の1文字を表すことが多く、「食パン*」と書けば「食パン」で始まる商品名がすべて当てはまります。
ここから基本情報!SELECT文の形
SQLで問合せを書くのがSELECT文です。どの列を取り出すかを SELECT 句に、どの表から取り出すかを FROM 句に、どの行を選ぶかを WHERE 句に書きます。SELECT 句が列を絞る射影に、WHERE 句が行を選ぶ選択に当たります。SELECT お客さま番号, 個数 FROM 予約 WHERE 商品 = '食パン' は、予約の表から商品が食パンの行を選び、お客さま番号と個数の2列だけを返します。
WHERE 句の中で条件を表し、真か偽かを返す要素を述語といいます。「= '食パン'」のような比較のほかに、範囲を表す BETWEEN、候補の一覧を表す IN、文字の形で探す LIKE などがあります。受取日 BETWEEN DATE '2026-09-01' AND DATE '2026-09-30' は両端の日を含む9月の1か月ぶんを、商品 IN ('食パン', 'あんパン') はどちらかの商品の行を選びます。
LIKE で使う、記号を含んだ検索の文字列をパターン文字列といいます。SQLでは「%」が0文字以上の任意の文字列、「_」が任意の1文字を表します。商品 LIKE '食パン%' と書けば、「食パン」「食パン(山型)」「食パン(ハーフ)」の3つが当てはまります。
基本情報の範囲ここまで
ここから基本情報!束ねて数える
複数の行の値から1つの値を計算する関数を集合関数、または集約関数といいます。行の数を数える COUNT、合計の SUM、平均の AVG、最大の MAX、最小の MIN があります。SELECT SUM(個数) FROM 予約 WHERE 商品 = '食パン' は、食パンの4行の個数 2+1+2+1 を足して 6 を返します。
お客さまごとの回数のように、値の同じ行どうしを束ねて数えたいときは、GROUP BY 句を使います。指定した列の値が等しい行を1つのまとまりにすることをグループ化といい、集約関数はまとまりごとに計算されます。6行の予約の表をお客さま番号でグループ化して COUNT(*) を求めると、C1 が3件、C2 が2件、C3 が1件です。
この中から「3回以上」の束だけを残すには、HAVING 句を使います。WHERE 句がグループ化する前の1行ずつに条件をかけるのに対し、HAVING 句はグループ化した後の束に、集計した値で条件をかけます。店長の二つめの問いは、次の1本の文になります。
| 句 | 書く中身 | この例での働き |
|---|---|---|
| SELECT | お客さま番号, COUNT(*) | お客さま番号と件数を出す |
| FROM | 予約 | 予約の表から |
| WHERE | 受取日 BETWEEN DATE '2026-09-01' AND DATE '2026-09-30' | 9月の予約の行だけを選ぶ |
| GROUP BY | お客さま番号 | お客さまごとに束ねる |
| HAVING | COUNT(*) >= 3 | 3件以上の束だけを残す |
| ORDER BY | COUNT(*) DESC | 件数の多い順にする |
6行の例で実行すると、結果は C1 の1行で、件数は3です。データベースはこの文を書いた順には処理しません。FROM の表から WHERE で行を選び、GROUP BY で束ね、HAVING で束を選び、SELECT で出す列を決め、最後に ORDER BY で順番を決めます。SELECT 句は先頭に書きますが、働くのは HAVING の後です。
基本情報の範囲ここまで
答えを見やすくするには、取り出した行の順番も決めます。指定した列の値を基準に、問合せの結果の行を小さい順(昇順)や大きい順(降順)に整列させることを並べ替えといい、SQLでは ORDER BY という句で指定します。予約の件数の多い順に商品を並べれば、いちばん上の行が店長の一つめの問いの答えです。6行の例なら、4件の食パンが先頭に来ます。
ここから基本情報!問合せの中の問合せ
「平均より多い個数を頼んだ予約」を探すには、先に平均を知る必要があります。SELECT文の WHERE 句や FROM 句の中に入れ子で書いた、別の SELECT文を副問合せ(サブクエリ)といいます。SELECT 予約番号 FROM 予約 WHERE 個数 > (SELECT AVG(個数) FROM 予約) では、かっこの中の副問合せが6行の個数の平均 10÷6≒1.67 を返し、外側はそれより個数の多い R01(2個)・R02(3個)・R04(2個)を選びます。
比べる相手が行ごとに変わる場合もあります。「そのお客さま自身の平均より多く頼んだ予約」を探すなら、平均はお客さまごとに求めなければなりません。外側の問合せが今見ている行の値を使いながら、行ごとに実行される副問合せを相関副問合せといいます。
このとき、同じ予約の表が外側と内側の両方に出てくるので、どちらの予約の表かを区別する名前が要ります。FROM 句などで表に付ける別名を相関名といい、FROM 予約 A と書けば、その表を A と呼べます。文は SELECT A.予約番号 FROM 予約 A WHERE A.個数 > (SELECT AVG(B.個数) FROM 予約 B WHERE B.お客さま番号 = A.お客さま番号) となります。
外側が R01 の行を見ているとき、内側は C1 の予約(2個・1個・1個)の平均 4÷3≒1.33 を求め、2個の R01 が選ばれます。R02 は C2 の平均 2 より多い3個なので選ばれ、C3 の R04 は自分の2個がそのまま平均なので選ばれません。答えは R01 と R02 で、ふつうの副問合せの答えとは R04 の扱いが違います。
基本情報の範囲ここまで
2-5そのほかのデータ操作言語
金曜の夕方、電話が2本続けて鳴りました。1本めは高橋さまから、「明日の食パンの予約を取り消したい」。2本めは鈴木さまから、「受け取りを土曜から日曜に変えてほしい」。その合間に、初めてのお客さまからクロワッサンの予約がネットで入ります。
表に入ったデータは、取り出すだけでなく、書き換えもします。新しい予約を1行足す挿入、すでにある行の受取日を書き換える更新、取り消された予約の行を消す削除の三つが、日々の書き換えの基本です。どの操作でも、対象の行を条件で正しく指定する必要があります。条件を書き損じると、関係のない予約まで書き換わってしまいます。
ここから基本情報!書き換えと権限のSQL
行を足すのがINSERT文で、INSERT INTO 予約 VALUES ('R07', 'C5', 'クロワッサン', 2, DATE '2026-10-24') のように、列の順に値を並べます。2-3で定めた検査制約や非NULL制約に反する値は、このときに拒まれます。
すでにある行を書き換えるのがUPDATE文です。鈴木さまの予約が R08 なら、UPDATE 予約 SET 受取日 = DATE '2026-10-25' WHERE 予約番号 = 'R08' で R08 の受取日だけが日曜に変わります。条件に合う行を消すのがDELETE文で、高橋さまの予約が R09 なら DELETE FROM 予約 WHERE 予約番号 = 'R09' と書きます。
利用者に操作の権限を与えるのがGRANT文です。GRANT SELECT ON レジ用 TO アルバイト は、アルバイトの利用者に、2-3で作ったレジ用のビューを読む権限だけを与えます。INSERT・UPDATE・DELETE の権限を与えていなければ、その利用者は予約を足すことも消すこともできません。与えた権限を取り消すには REVOKE文を使います。
基本情報の範囲ここまで
2-6埋込みSQL
ネット予約のお客さまには、受取日の前日の夕方に確認のメールを送っています。いまは店長が予約の一覧を見ながら、1通ずつ手で書いています。ミナトは、ネット予約のプログラムに、翌日受け取りの予約を表から読み出してメールを送る処理を足せないかと考えました。
データベースを使うのは、画面の前の人だけではありません。予約を受け付けるプログラムも、メールを送るプログラムも、中でSQLの文を実行して表を読み書きしています。ただ、問合せの結果は何行もの表で返ってきます。メールは1通ずつしか送れないので、プログラムの側では、その結果を1件ずつ取り出して扱う工夫が要ります。
ここから基本情報!プログラムの中のSQL
プログラムのソースコードの中にSQLの文を直接書き込んで使う形を埋込みSQLといいます。埋込型SQLと書く資料もありますが、シラバスの表記は「埋込みSQL」です。SQLの文を書き込まれる側の、C や COBOL などのプログラム言語を親言語(ホスト言語)と呼びます。埋込みSQLのプログラムは、コンパイルの前にプリコンパイラというプログラムに通し、SQLの文をデータベースを呼び出す命令に置き換えてから、親言語のコンパイラにかけます。
SQLの文とプログラムの間で値を受け渡すには、親言語の変数を使います。埋込みSQLの文の中で使う親言語の変数をホスト変数といい、文の中では :day のように前にコロンを付けて、表の列の名前と区別します。プログラムが明日の日付をホスト変数 day に入れてから問合せを実行すれば、明日受け取る予約が返ってきます。
基本情報の範囲ここまで
ここから基本情報!1行ずつ読むカーソル
問合せの結果は何行もありますが、ホスト変数には1回に1行ぶんの値しか入りません。この食い違いを埋めるのがカーソルで、問合せの結果の行を1行ずつ順に指し示し、取り出せるようにする仕組みです。
カーソルを使うには、まず DECLARE で、カーソルの名前と、そのカーソルで読む問合せを定めます。次に OPEN でカーソルを開くと問合せが実行され、結果の行を先頭から読み出せる状態になります。この二つが、シラバスのカーソルの宣言や操作の開始に当たります。開いたカーソルから次の1行を読み込み、ホスト変数に入れるのがFETCH文で、FETCH kakunin INTO :name, :item のように書きます。
FETCH を行が無くなるまでくり返し、1行読むごとにメールを1通送ります。読み終えたら CLOSE でカーソルを閉じます。DECLARE・OPEN・FETCH・CLOSE と進むこの一連の操作がカーソル操作です。
一方、カーソルを使わない非カーソル操作もあります。結果が1行に決まる問合せ(SELECT … INTO で直接ホスト変数に入れる形)や、INSERT・UPDATE・DELETE の文は、カーソルを使わずにそのまま実行します。「受取日を過ぎた予約に、受け取り済みの印をまとめて付ける」なら、UPDATE文1本で当てはまる行が一度に書き換わり、1行ずつ読む必要はありません。
基本情報の範囲ここまで
ここから基本情報!実行時に文を組み立てる
ここまでの埋込みSQLの文は、プログラムを書く時点で中身が決まっていました。これに対し、実行する時点でSQLの文を組み立ててから実行する方式を動的SQLといいます。店長が管理画面で「商品」「受取日」「お客さま番号」のどれで探すかを選べるようにすると、WHERE 句の中身が選び方で変わるので、文をその場で組み立てることになります。前もって中身が決まっている文は、これと対比して静的SQLと呼ばれます。お客さまが入力した文字をそのまま文につなぐと SQLインジェクションの攻撃を受けるおそれがあるので、入力は値として渡す書き方にします。
プリコンパイラを使わず、関数やメソッドの呼出しでデータベースにSQLの文を渡すやり方もあります。Java のプログラムからデータベースに接続し、SQLの文を送って結果を受け取るための標準のAPIがJDBC(Java Database Connectivity)です。データベースの製品ごとに用意されたドライバを入れ替えれば、プログラムの書き方をほとんど変えずに別の製品につなげます。
基本情報の範囲ここまで
次の月曜の夕方、ミナトは管理画面に1本の問合せを打ち込みました。9月の予約を、お客さまごとに束ねて数え、3回以上の人だけを、回数の多い順に。お客さまの表とつないでおいたので、返ってきた14行には名前と住所が並んでいます。店長はその日の閉店後に、14枚のはがきを書き終えました。いちばん人気のパンは、やはり食パンでした。
台帳のころは1日がかりだった二つの問いは、行を選び、まとめて数え、条件で残し、並べ替え、表をつなぐという操作の組み合わせで答えが出ました。表に作り直した意味は、データをきれいに並べたことよりも、問いを操作の組み合わせとして書けるようになったことにあります。書きさえすれば、数え上げはデータベースが引き受けます。
はがきを出し終えた店長が、カレンダーを見て言いました。「12月になったら、クリスマスケーキの予約が始まるね。去年は電話とネットで一晩に80件も来たけど、残り1台のケーキに二人が同時に予約したら、この表はどうなるの」。ミナトは、まだ答えられませんでした。