なぜですか

書きたいと思ったことを書きます

「食べ始まる」考

先日、こちらの新書を読んだんです。

日本語という外国語 (講談社現代新書)

日本語という外国語 (講談社現代新書)

その中に、以下のような記述がありました。

不思議なことに終わりの局面では「終わる」「終える」と両方の補助の動詞が使えるのに、始まりの局面では「食べ始める」は言えても、「食べ始まる」とは言えません。

たしかに著者の仰る通りで、「食べ始まる」はふつう使わない。この記事はそれがなんでやねんという話です。明確な答えが書いてあるわけではないのでお気をつけください。

「終わる」「終える」、「始まる」「始める」の違い

「終わる」は、ラ行五段活用の自動詞で、「終える」はア行下一段活用の他動詞。 「始まる」は、ラ行五段活用の自動詞で、「始める」はマ行下一段活用の他動詞。 どちらのペアも意味はほとんど同じどうしで、それぞれ物事の終了と開始を意味する動詞ですが、他動詞のほうが、それのかかる主体にとって意識的に終了・開始を行っているニュアンスが含まれています。例えば「そろそろ試合が始まる」の場合は他の誰かによって開始の合図があるように思われますが、一方で「そろそろ試合を始める」の場合は話者自身によって試合が始められるように思われます。

これだけなら、「食べ終わる」のように「食べ始まる」を使っても何の問題もないように思えてきます。念のため、辞書の上では意味も確認してみましょう。太字のものは、自動詞・他動詞いずれにもある意味です。

終わる

終(わ)る(おわる)の意味 - goo国語辞書

  • 続いていた物事が、そこでなくなる。しまいになる。済む。
  • 2 (「終わった」「終わっている」の形で)廃れる。人気が衰える。
  • 3 (「…におわる」「…でおわる」などの形で)期待された結果が得られないまま、それが最後の状態になる。
  • 4 動詞の連用形に付いて、動作・作用が完結する意を表す。…しおわる。…てしまう。
  • しまいにする。終える。
  • 6 生命が尽きる。死ぬ。

終える

終える(おえる)の意味 - goo国語辞書

  • 続けてきたことを済ませる。終わらせる。
  • 続いてきたことが終わる。

終わる・終えるは、終わるがより大きな意味を持ち、終えるを内包しているような関係(終わる⊂終える)になっています。

始まる

始まる(はじまる)の意味 - goo国語辞書

  • 物事が行っていない状態から行う状態になる。行われだす。
  • 新しく起こる。新たに発生する。
  • 3 起因する。起源をもつ。
  • いつものくせが出る。
  • 5 (「…ても始まらない」の形で)むだだ。手遅れだ。しようがない。

始める

始める(はじめる)の意味 - goo国語辞書

  • 物事を行っていない状態から行う状態にする。行いだす。
  • 新しく起こす。新たにつくる。
  • いつものくせを出す。
  • 4 (動詞の連用形などに付いて)その動作が行われだすことを表す。

始まる・始めるについてもほとんど同様で、始まるが始めるを内包する関係のようにも、部分共通のようにも思えます。これを見ても、「動詞+終わる」が言えて「動詞+始まる」が言えないのは、いささか不釣り合いな気がします。

言い換えて考えてみる

「食べ終える」は「食事を終える」に、「食べ終わる」は「食事が終わる」に言い換えることができます。同様に以下のように言い換えてみましょう。

  • 「食事を終える」(食べ終える)
  • 「食事が終わる」(食べ終わる)=「食事を終わる」
  • 「食事を始める」(食べ始める)
  • 「食事が始まる」(食べ始まる)=「食事を始まる」

こうして並べてみても、やはり「食べ始まる」はなんらおかしくないどころか、むしろ「始まる」を他動詞として「食事を始まる」と用い、その言い換えとして「食べ始まる」を使えないことのほうがおかしく感じられてきますよね。今こそ「食べ始まる」を使い始まるに相応しい時期なのかもしれません。

こういった変化には違和感があるかもしれませんが、かつて「大き(い)」の対義語として「小さな」しか無かった時代に、語尾の据わりを良くするために「大きな」「小さい」を発明し、これまで使ってきたように、こういった後からの発明はおかしなことではないはずです。

いざ使ってみる

ということで、今日から「動詞+始まる」の布教活動を始めようと思います。それを使うシチュエーションを考えてみましょう。

「食べ始まる」の場合

A「(電話がかかってくる)はい。Bさん?いつ帰ってくるの?」

B「もうすぐ帰るよ。みんなは晩ご飯もう食べた?」

A「ううん、ちょうど食べ始まるところだよ」

違和感があるかもしれませんが、最後の文を同じ自動詞の「うん、もう食べ終わるところだよ」にすれば違和感がありません。ということは、使い慣れてくれば「食べ始まる」も違和感が薄れてくるのではないでしょうか。とはいえ、これはもし「食べ始める」でもおかしくない、というのもその通りです。同様に「もう食べ終わるところ」が「もう食べ終えるところ」だったとしてもおかしくはないですが、何か独自の使い方を見つけたいところですね。

「歌い始まる」の場合

A「ライブ、まだ始まらないのかな?」

B「あ、見て。バンドの人が出てきたよ」

A「お、もうすぐ歌い始まるね」

この場合、「歌い始まる」ほどではありませんが、「もうすぐ歌い始めるね」にも少し違和感があります。「名詞+が+始まる」で「歌が始まるね」だとそこまで違和感はありませんが、「動詞+始まる」で同じ表現ができても不都合はなさそうです。

なぜ「動詞+始まる」を使わないのか

ここまで見てきた通り、「動詞+始まる」は文法的にもおかしなところはありませんし、使おうと思えば使うこともできそうな用法でした。しかし、使われていません。なぜでしょうか。答えが明確にあるわけではないですが、予想してみましょう。

他の表現で十分だから

上で見たように、「食べ始まる」は「食べ始める」(動詞+始まる)で十分ですし、「歌い始まる」は「歌が始まる」(名詞+が始まる)で十分伝わります。あえて区別する必要がなかったので、「動詞+始まる」が使われることもなかった可能性がありそうです。しかし、だとしたらどうして「動詞+終わる」と「動詞+終える」とが共存しているのか、という疑問が残ってしまいますね。

開始の感覚の問題

私たち日本人の無意識のなかには、『動作の開始はつねに意識的・自発的だから他動詞「始める」でしかありえないが、動作の終了は意識的・自発的である他動詞「終える」以外に、何らかの外的要因によって外発的に自動詞「終わる」こともありえる』という感覚があるのかもしれません。今でこそ「食べ終わる」「食べ終える」に大きな違いは無いものの、昔は内発的か外発的かによって使い分けており、一方で「始める」「始まる」にはその必要がなかったのではないでしょうか。

開始の責任の所在の問題

先程は動作の終了のほうに重きを置いていたのに対して、こちらは動作の開始に重きを置いた考え方です。動作を終了した人は誰であっても構わないから自動詞的な表現「動詞+終わる」が許されてきたが、動作の開始については誰が始めたのかを明確にするため、「動詞+始まる」が許されてこなかった、という可能性があるかもしれません。

結局答えは無いんですけど

ここまでに、いくつか可能性を挙げてきました。念のため以下におさらいします。

  • 他の表現で十分だったから
  • 動作の開始は主体的でしかありえないから
  • 動作の開始は誰が行ったのか明確でなければならないから

結局のところ、このどれが正解なのか、またこれらのうちのどれでもなく、他に答えがあるのか、はたまたこれらのうちのいくつかが複合しているのか、わかりません(きちんと調べていないので)。でも、こういった日常的に見逃している不思議に、気付かせてくれる本と出会えたのは幸せだったなあと思っています。未読の方はぜひ一度読んでみてください。

それにしても、プロの言語学(日本語学)の方はこういった問題をどうやって追いかけているのでしょうかね。気になったので、このGWはそのへんの勉強をしてみたいと思います。

紙の辞書、めちゃくちゃ良い

先日、紙の辞書を買ったんです。そう、イタリア語のですよ。これがめっちゃいい。21世紀なのに、紙、めっちゃいい。

この記事はそれがなんでやねんっつー話ですわ。

紙以前の時代

辞書を買うよりも前のころ、イタリア語を読んだり書いたりするのに使っていたのは、以下のものでした*1

Google翻訳

イタリア語←→日本語に設定して使用します。基本的には良いんですが、たまに翻訳に問題があります。体感ですが、正解率は6割ぐらい。イタリア語から英語、英語からイタリア語への翻訳精度は割と高いそうなので、やはり語族の壁は厚いということなのでしょう*3

それから、イタリア語に限らず英語でも、Google翻訳は読んだそのままの音をカタカナ語として翻訳結果にすることがあります。また、大文字で表記すると、大文字のアルファベットがそのまま翻訳結果になっていることもあります。こういった不思議な挙動は、最近とくに見かける頻度が高くなってきたような気がします。

f:id:mizunokura:20180311115030p:plain

f:id:mizunokura:20180310231157p:plain Googleの謎、あるいは自然言語の不条理

無償で使えるものに文句を言い過ぎるのも良くないと思いますが、さらに言えば、検索語に対して意味がひとつしか表示されないことにも不満があります。日本語でもひとつの単語に複数の意味があることが少なくありませんし、イタリア語でもまたそうなのです。もっとも、Google 翻訳は辞書ではなくあくまで翻訳がその領分なので、辞書的な機能までも求めるのは酷かもしれません。

bab.la

イタリア語は屈折語なので、単語が活用しますし、中でもとくに動詞の変化が激しく、ひとつの単語について以下の活用形が存在します。

単純時制

  • 直説法現在
  • 接続法現在
  • 命令法現在
  • 条件法現在
  • 直説法未来
  • 直説法半過去
  • 接続法半過去
  • 直説法遠過去

複合時制

  • 直説法近過去
  • 接続法近過去
  • 条件法近過去
  • 直説法先立未来
  • 直説法先立過去
  • 接続法先立過去
  • 直説法大過去

不定

  • 不定
  • ジェルンディオ
  • 過去分詞

しかも、不定法以外の活用形については人称ごとに異なる変化をします。 例:「avere(持つ)」の変化

これらすべてをいきなり覚えるのは無理なので、よく使うものから覚えるようにしています。とはいえ書くときや読むときには、何がどの活用形かを知る必要が出てきます。そんなとき、 bab.la で検索窓に活用後の形で入力すれば、不定形などを一覧で見ることができ、便利なのです*4

Reverso Traduzione

このサイトは主に自分で文を作るときに使っています。単語の意味や活用形はわかっていても、その自然な使われ方は、例文を見て確認しなければわかりません。

日本語で例えるなら、焼肉を食べたことを伝えたい場合、「焼肉」「食べる」はわかっていても、その間にどんな助詞を入れるべきかわかりません。「焼肉が食べた」や「焼肉の食べた」や「焼肉と食べた」はおかしいですし、「焼肉は食べた」は正しくなる場合もありますが、この場合は「焼肉を食べた」が最も適しています。

こういう実際の使われ方を見るのに重宝しています。

WordReference.com

辞書サイトです。イタリア語の単語の意味が英語で出てきます。これはこれですごく使いやすいんですが、その単語に表記の似た別の単語が知りたい場合、関連して探す場合の使い方が難しくなります。

左側に「Vedere Anche:」の欄があり、近い表記の別単語が並んでいるのですが、それらの意味までひとつひとつ開かなければはわかりません。開いたとしても、英語なので、知らない単語だった場合はさらに英語→日本語の辞書サイトを参照したり、Google 翻訳を使ったりする必要があります*5

紙の辞書を買ったら長男が大学に合格しました

もちろん、紙の辞書のデメリットは全部知っています。

  • 引くのに時間がかかる
  • 重い
  • 有料

ほかにも「濡れたら終わる」とかって言いますけど、実際そういう状況で使うことはないので無視します。たしかに重い。そして引くのに時間がかかる。買うのに金もかかる。けれど、自分にとってはメリットがそれを上回ります。

  • 同音異義語が載っている。
  • 意味が日本語で書かれている。
  • 意味の記載優先度が、和語・漢語>カタカナ語になっている。
  • 似た表記の単語が意味と一緒に一覧になっている。
  • 慣用表現が記載されている。
  • 手に持って開いて読める。
  • 紙のにおいと手触りがある。

上4つは先述の、辞書購入前の問題点を解決してくれるものです。辞書購入後は、紙の辞書と、bab.la と、Reverso Traduzione を基本的に使い、Google 翻訳を作業全体の補助に使っています。さよなら WordReference.com 、ありがとう WordReference.com。

紙の辞書ってなんかあれなんですよね。言わば、自分の興味範囲の単語が会話に出てくると急に早口になるオタクに似てるんです*6。こちらが「待って、そこまで聞いてないんだけど」ってことを、延々と話し続けてくれる。単語を引くたびにそうだから「あーはいはいそれね。前も聞いた」って感じで耳から頭に残る。そのおかげで覚えられる。ありがとうオタク!!

紙で出来ていることも個人的にポイントが高く、辞書特有の紙のにおいがするので引いていて嬉しくなります。多少重たいけど、紙の重さなら不思議と気になりません。なぜって、脳内麻薬が出るからね。デメリットに書いた有料であることすらも、所有欲を満たしてくれて嬉しいので、ちょっと本気で頭がおかしいのかもしれません。

そういうことに気付かせてくれることもまた、紙の辞書のメリットなのかもしれませんね。

f:id:mizunokura:20180311015933j:plain

*1:今も使っているものもあります

*2:など他言語もあるが、日本語は翻訳に難あり

*3:日本語が印欧語族っていう可能性もゼロではないけどね。

*4:日本語でこういうサイトあるのかな?

*5:英語勉強せいっつー話ではあるんですが

*6:自分もたまにやっちゃうし、ディスではないですよ

本を読む理由

コミュシルというサイトで以下の記事を読んだのをきっかけに、あらためて「本を読む理由」について考えました。

commusiru.jp

今の自分が「本を読む理由」

2017年の夏ごろ、仕事で使うこともあり、日本語の文法についての考え方や、校正・編集の方法について知る必要が出てきました。このとき自分は毎週のように神保町へ出向き、古本屋の新書コーナーなどで日本語関連の書籍を買い求め、古本が出回っていない書籍については新刊本屋やWebの通販を利用したり、人に借りたり、kindle形式の書籍などを購入したりして、ひたすら読むことで知識を得ていました。

つまり、今の自分にとっての「本を読む理由」は、「知りたいから」だといえそうです。

ただし、例えば海に潜ってみたときや、苦労して山に登って朝焼けを見たときなどの、実際に自分で体験して、自分の感覚を通して知るべきであろう実感については、「本を読んで得よう」という気にはなりません。他人によって書かれた本の中には、その人の知らないはずのことである自分の実体験について書かれているはずがありません。著者の実体験についてや、著者に限らない一般的な物事が書かれています。だから正しくは、「一般的な物事や、もしくは著者の感覚を通した経験について知りたいから」本を読むのだといえるのだと思います。

無意識での選択

では、「一般的な物事や、もしくは著者の感覚を通した経験について知りたい」と感じたとき、どうして本を手に取るのでしょうか。世の中には本以外にも知識の仕入先はあります。さらに本の中にも、漫画本や絵本、新書、辞書など多くの種類があります。それらの選択肢の中から、自分は無意識に本を、書籍を選択していました。なぜでしょうか。

それは、本でその知りたいという欲求が解消されるということを、あらかじめ知っていないとできないことです。本を選ぶからには、これまでの人生のいずれかの時点で、そのことを学習しているに違いありません。

「知りたい」のはじまりのころの自分

1987年、当時2〜3歳だったころの自分が何かについて「知りたい」と思ったとき、「本を読もう」とはおそらくはなりませんでした。当時は書籍を自分で読むことも難しく、また何か新しい物事を教えてくれるのはほとんどの場合両親だったので、自然に「両親に教えてもらおう」となったと思います。そこで彼らに「なんで?」と質問し、欲求を解消していました。

絵本も読んだり読みかせてもらったりはしていたのですが、自分にとっての絵本は絵を見る目的が第一のものであって、知識はそのついでに得られるものという位置づけだったのです(たぶん)。また絵本は、タイトルなどから得られる知識の内容を予測しにくいように思います。もし目的を持って絵本を読んだとしても、セレンディピティな知識との出会いはあれど、おそらく本来の目的には到達しにくいでしょう*1

両親への質問から、書籍へ

かつて質問を繰り返していたところから、どうして本に、とくに書籍に、知識を得るための方法が変わったのでしょうか。それは自分の場合、2種類の本との出会いが助けになっていたように思います。つまり、図鑑と昔話集です。

図鑑は、ほとんどの場合タイトルと内容が一致しています。「鳥」というタイトルの図鑑なら鳥が載っていますし、「動物」であっても同様に動物だけが載っています。いきなり自動車について触れられてはいません。また、図鑑は図が豊富に載っているため、見慣れていた絵本の形式と似ていることから精神的なハードルが低く、「本には一般的な知識が体系立てて掲載されている」という意識に、スムーズに移行できたような気がします。

昔話集は、まずそれがどういう形式の本かというと、100話あまりの昔話が、見開き1ページの中央に絵、その周囲にお話の文、というフォーマットで書かれているものでした。とても絵本に似ているのですが、文の量が比較的多く、また漢字もふりがな付きで豊富に載っていました。母親の五十音教室を卒業してすぐの自分には少し荷が重かったような気もしますが、語彙が増えるにつれて少しずつ読解できるお話も増え、そのことが文を読むことの楽しさに拍車を掛けていたように思います。

この2種類の本を足がかりにして、だんだんと読める本の種類が増えていったように思います。

本を読んでもらうには

しばしば子らの親たる人々が、「子どもに本を読ませたい」と言っているのを耳にします。なぜでしょうか。そして、どうしたらいいのでしょうか。

自分の持っている本を読む理由に照らし合わせると、「一般的な物事や、もしくは著者の感覚を通した経験について知ってもらいたい」から、要するに「賢くなってもらいたい」または「人の気持ちのわかる人になってもらいたい」からなのでしょう。もし理由が明確で、読ませたい書籍の形式までがはっきり決まっているのなら、それに向かって、成長に合わせた段階を踏んだ種類の本を与えていき、慣れていってもらうのが良いのではないかと思います*2

自分の場合は、絵本からいきなり児童書へ移行しようとして、一度失敗しました。それは、「絵を見る」経験から「文を読む」経験に移行するにあたって、文の量が多すぎたことや、そもそも内容に興味が無かったことなどが関係しているのではないかと思います*3

「活字を追いたい」という欲望

最後に。

とくに何かを知ることを目的とせず、ただ印刷された文字を読みたいと思うことがあります。他の人が言っているのを見ることがあるので、凡そ一般的な欲求なのかと思っていますが、ピンとこない人もおそらく多いでしょう。本に向き合っている静かな時間が好きだったり、文意を汲み取るときの脳内のはたらきが好きだったり、脳内で音読する声が好きだったり、人によって理由は様々なのではないでしょうか。

自分は文字を見ると落ち着くことが多いので、頭の中がいっぱいいっぱいになったときなどに活字を追いたくなります。とくに、文の流れに無理がなく、文法的に大きな誤りがなく、誤字脱字のない文章を読んだときには、まるで良い音楽を聞いたときのような心地よさを覚えます。

ということは自分の場合、「知りたいから」以外にも、「心地よさを求めて」本を読んでいるのかもしれません。

*1:そして自分の中でのこの位置づけには、今では漫画がいるような気がします。

*2:絵本→図鑑→昔話集のような短文…というように

*3:その本のタイトルは覚えてるんですが、未だに読んでいません

連続を失敗

ある夜、会社から帰ろうとして玄関で靴を探したとき、そこに自分の靴が無いことに気付いた。

決していじめられているわけではなくて

今働いている会社はいわゆるオフィスビルではなくかなり広めの一軒家なので、玄関では靴を脱ぎ、社内はスリッパで歩く必要がある。そのために帰宅の際には玄関で自分の靴を探して履くのだが、そこにあるはずの自分の靴がなかった。いや正確には、自分の靴だと思う靴はあるが、その確信が持てなかった。

手に取り、何度か見てみる。違和感は消えない。こんなタグは付いていたっけ。こんな色だったっけ。こんなにロゴは白かったっけ。ソールはこんな色だったっけ。

履いてみる。でもやっぱり変な感じがする。ニューバランスゴアテックスが使われているものだから、十中八九これだと思うし、社内にほかに28センチの靴を履く人がいたとしても、まったく同じ靴を買って履いている可能性は非常に低いはずだ。そう考えても、やはり他人の靴を履いているような感覚が消えなかった*1。他に自分の靴だと思えるものがなかったので、「これは自分の靴だ」と言い聞かせて履いて帰った。

あの夜から1週間ぐらい経過したが、誰からも「自分の靴がなくなった」という話は聞かない。やっぱり自分の靴だと思われるが、違和感はうっすらとまだ自分の中にある。

信号を待っているときの違和感

まだ違和感の話は続く。信号を待っているとき、そしてそれが青に変わったとき、「本当の自分は痴呆老人で、自分自身をまだ思考がしっかりしている30代なんだと思い違えているだけなんじゃないか」と考えることがある。つまり目の前の青信号は実際には赤であり、現実の自分は今まさに死のうとしているのだ。

実際にはそうでないことはなんとなくわかっている。しかし、そうでないと証明することももちろんできない。自分の認識している現実を現実として受け止めて生きていくことしかできないのだとしたら、なんと我々は認識の前に無力だろうか。そんな頼りの認識が揺さぶられたとき、どうしようもなく振り回されてしまうのは、しかしながらさもありなんという思いがする。もはや虚構の青信号を信じ、現実の轢死を受け入れるしかない。

それからというもの、赤信号が青に変わると、同じように自分を確かめてしまうようになった。その確認に意味はないけれど。

知っているものしか見られない

世のメジャーが紙の辞書から電子辞書に移りつつある昨今*2、「電子辞書では自分が引きたいと思った項目しか見られない。いっぽう紙の辞書なら、同じページに並んでいる項目も一緒に目に入る。そのぶん知識を広げることができるのに、機会損失になってしまう!」というような話をよく耳にする。

自分は「電子辞書も紙の辞書も良し悪しだから、使い方にあわせて選べばよい。すべてが紙または電子になる必要はない」と考える、平凡で、面白みのNASA*3を持ちあわせた人間だが、ここではその是非について話したいわけではない*4

かつて人類が Yahoo! 上で検索を行っていたころ、Webサイトはちょうど図書館の本のようにジャンルごとに分類され、並べられていた。利用者は見たいジャンルのページを開き、そこに並んだ Webサイトから見たいものを選んでアクセスしていた。ときには隣に(上や下に)並んでいるWebサイトが気になり、そこを見ることもした。目的のもの以外のWebサイトは、自分の知識の外にあるものだった。

現在はご存知 Google にWeb検索が取って代わられ、自分の知っている単語の組み合わせを用いて情報を検索するようになった。知識の外にあるものは、洪水のように誰かの興味が流れていく TwitterFacebook などの SNS、または Wikipedia などの情報が羅列されているWebサイトで摂取するように、自分の場合はなっている。

かつて誰かが「これからの時代はなんでも検索できるから何も覚えなくていい」と言説しているのを聞いたが、実際にはそうはいかず、「少なくとも検索するための単語は覚えなくてはならない」という状態になっているように思し、その検索語の種類や使い方を適切に身に着けていることが、検索したいものにたどり着くまでの速度に影響しているような気がする。また、言葉についての知識は、検索結果から情報を読み取るためにも重要だ。人間が言語を用いる限り、言語能力は重要であり続けるだろう。

いったい何の話だ

時間が連続しているものであり、左(または後方)からやってきて現在を追い越し、右(または前方)へ流れていくというイメージや感覚は、信じるに足るものなんだろうか*5

ふいに目がさめて、状況を確認し、たとえばそこが祖父母の家で、いとこたちがまだ中学生で、自分は肉体的に7歳程度、階下から母親の呼び声がし、居間で「今日のお昼は外で食べよう」という会話がされていたなら、さっきまでの自分の現実はじつは夢であって、こっちが現実なんだと、少なくない違和感を押し込めてしまう可能性が無いと言いきれない。

靴を失くすまでの自分は、可能性が無いと言い切ったかもしれない。信号が青に変わるまでは。

*1:おもしろいことに、人の靴だと思うとなんか汚い感じすらしていた

*2:もう移行は完了したのかもしれないけど

*3:アメリカ航空宇宙局

*4:電子辞書でも単語が画面に並んで出てくるものもあるし

*5:ちなみにその『時間』は青い

「データベースが使えるようになりたい」と言われたので書いた

ある日うちの奥さん(エンジニアではない)から、「データベースが使えるようになりたいから教えてほしい」と相談を受けた。

一口にデータベースと言ってもいくつかあるので詳細を聞いてみると、彼女が意欲を示している「データベース」とは MySQLPostgreSQL のこと*1で、今の職場に残されたレガシーなデータベースから古い情報を取り出すのに使うとのことだった。MySQLPostgreSQL などのデータベース*2にはいくつかの機能があるが、情報の取り出しだけができればとりあえずは良いらしい。とはいえ「データベース使えますよ」と自信を持って言うために、情報の追加や更新、削除もできるようになっておきたい、ということだった。

そこで、そもそもデータベースはどんなもので、どういうことができるのか、どうすればできるのかを以下に簡単にまとめてみる。以下は奥さん向けに書いたものなので、普段エンジニア以外が耳慣れない言葉などはなるべく一度説明してから登場させるか、一度並べてから説明を後述することにする。ではいってみよう。

使う側にとっての「データベース」って何だろう

データベースの用途を一言で表すなら、「情報を取り出しやすいように整理して置いておける場所」*3だと思う。言わば、たくさん本をしまっておいてすぐに取り出すことのできるとても便利な本棚と、その本棚から本を取ってきてくれる超素早い管理人がセットになったようなもの。

データベースを構成するもの

  • 本棚
  • 管理人(めちゃくちゃ素早い。本棚の目録を持っている)

f:id:mizunokura:20180211124134p:plain

※ 目録がなかったので代わりに金棒になりました。

「データベースってエクセルとかスプレッドシートと同じようなもんなんじゃないの」という話を聞くことがあるのは、その発言をする人がExcelGoogle Spreadsheetsを「情報を取り出しやすいように整理して置いておける場所」として使っているからで、要するにその人にとっての用途が同じだから「同じようなもん」だと感じるのだろうと思う。ExcelGoogle Spreadsheetsでできるように、「検索」機能を使って文字の部分一致検索をしたり、特定のセルの内容に一致する他のシートの行を引っ張ってきたりということは、データベースでもできる。

使うための用意

データベースを使うためには、以下のものが必要になる。すでにデータベースが用意されている環境なら、以下の中から不足しているものだけを用意しよう。説明していない言葉が出てくるが、ここでは列記するだけで後で説明する。

  • データベースサーバ
  • クライアント

データベースサーバとクライアントって何?

『データベースサーバ』は上でのたとえで言うところの、本棚と管理者と、それらがいる建物のことを指す。『クライアント』はあなたの召使いで、あなたが「あの本取ってきて」と頼むとデータベースサーバに出かけていき、管理者に「あの本取ってきて」と話しかけ、そこで本を見てその内容のコピーを取り、あなたのところまで持ち帰ってくる。他の人が見に来る可能性があるので、本そのものを持ち帰ることはできない。

この仕組みは、インターネットで見ているWebサイトと同じだと考えることもできる。あなたが今使っているブラウザ(ChromeSafariIEやEdgeなど)が『クライアント』で、彼に*4http://mizunokura.hatenablog.com/ という場所に書かれている内容を取ってきて」と頼むと、その場所にある建物まで出かけて行き、そこにいる管理者に見せてくれるよう話しかけ、見せてもらった内容のコピーを取り、あなたのところまで持ち帰ってくる。

f:id:mizunokura:20180211131353p:plain

データベースサーバの用意

すでにどこかにあることがわかっている場合

その場合、以下の情報を集める必要がある。ここでも先に列記してから後で説明する。

  • データベースサーバの種別
  • アドレス
  • 認証情報
  • ポート番号

データベースサーバの種別

データベースサーバにはいくつかの種類がある。

その他にもたくさんの種類があるが、市場シェア割合が高いのはこの4つ。それぞれに異なるクライアントを使用するので、種別を知っておく必要がある。こればっかりは聞くしかないので識者に質問しよう。種別が判明したら「MySQL クライアント インストール Mac」などで検索し、自分のパソコンにクライアントをインストールしよう。

アドレス

建物で例えるなら住所のこと。クライアントが出かけて行く先。IPアドレス(192.168.1.1 とか)だったり、ホストアドレス(db0001.local とか)だったりする。これも聞くしかないので聞こう。

認証情報

クライアントが建物に入っていくための名前(ユーザー名)とパスワード。これも聞くしかないので聞こう。

ポート番号

じつは、建物にはたくさん出入り口が用意されている。しかしながらセキュリティを確保するために、指定された出入り口以外を使うことはできないことになっている。MySQL の場合、初期設定では 3306 番の出入り口を使用する。これも聞くしかないので聞こう。聞いて「わからない」と言われたら、たいてい 3306 番だと思っておそらく問題ない。

とりあえず自分が使えればいい場合

その場合は、自分のパソコンの中に建物を建てて本棚を置いて管理人を連れてくることができる。データベースサーバの種別を決め、パソコンにインストールしよう(ググろう)。

みんなで使いたい場合

その場合も「自分が使えればいい場合」と考え方はほとんど同じだが、自分のパソコンの中ではなく、みんなが使うことのできる共通の場所に建物を建てる必要がある。その方法などなどを説明し始めると話が逸れるので割愛する。

クライアントの用意

上述の通り、データベースサーバの種別ごとに異なるので、種別を調べてから「MySQL クライアント インストール Mac」などで検索し、自分のパソコンにクライアントをインストールしよう。

管理人と話そう

データベースサーバとクライアントの準備ができたら、さっそく建物の中にいる管理人に挨拶してみよう。都合上ここからは MySQL に絞って説明していく。

建物に入ろう

データベースサーバという建物の中に入っていくためには、上述の「アドレス」と「認証情報」と「ポート番号」とを使う必要がある。コマンドラインクライアント(黒背景に白文字の画面で使用するクライアント)の場合、ターミナルに以下を入力($は入力しない)して、エンターキーを押して実行する。

$ mysql -u ユーザー名 -h アドレス --port ポート番号 -p

このような、パソコンに対する命令文を「コマンド」と呼ぶ。

また、上記のコマンドのポート番号については初期設定で3306になっているので省略できる。さらに、パスワードが設定されていない場合にも省略でき、自分のパソコンの中に建物を建てた場合アドレスも省略できるので、以下の内容で実行することができる。

$ mysql -u ユーザー名

自分でデータベースサーバをインストールした場合、ユーザー名は、とくに変更していないのであれば root になっている。

例えば以下のような表示が出てくれば、建物に入ること(ログイン)に成功している。

$ mysql -uroot
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 4
Server version: 5.7.21 Homebrew

Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql>

上の mysql> の部分がクライアントの待機状態を示す文字列で、「管理人になんて言ってきましょうか」と自分に聞いてきてると考えると良いかもしれない。

パスワードの入力を促される場合

実行時に -p を付けている場合、以下のようにパスワードの入力を促される場合がある。パスワードを入力しよう。画面に入力している文字が何も表示されないが、そのまま入力してエンターキーを押せば問題ない。

$ mysql -u ユーザー名 -p
Enter password:

パスワードを間違えていると以下のようなエラーメッセージが表示される。

ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: YES)

また、以下のようにパスワードをコマンドに含めてしまうこともできる。

$ mysql -u ユーザー名 -pパスワード

MySQL サーバが起動していない場合

以下のようなエラーメッセージが出る場合、MySQL データベースサーバが起動してない。例えるなら、建物も本棚も管理人もいない状態である。

$ mysql -uroot
ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2)

Mac で homebrew を使ってインストールした場合、以下のコマンドを使って起動することができる。

$ brew services start mysql

建物から帰ろう

いつでもクライアントを終了できるように、その方法を確認しておこう。mysql> の表示に続けて quit と入力し、エンターキーを押せば良い。

mysql> quit

また、 Ctrl+D キー でもクライアントを即座に終了することができる。

建物の中でできること

MySQL データベースサーバでは、大きく分けてふたつのことができる。どちらもクライアントが管理人に対して話しかけることで効果を発揮する。

  • SQLコマンドの実行
  • MySQLコマンドの実行

SQLコマンド

SQLコマンドとは、データベースを操作する際に使用するために作られた命令文の集合*5で、最初のバージョンが発表されてから各データベースサーバがそれぞれに取り入れ、さらに独自の拡張を行ってきたもの。あまりにも各データベースサーバごとに異なってしまうのを防ぐために作られた標準統一規格が存在するので、基本的にはある程度共通しているが、異なっている部分もある。

大きく分けて、以下の操作ができる。カッコ内は本棚の例えをした場合のイメージ。

  • データ定義(本棚や、本棚に置く内容を決めたり、本棚を削除したりする)
  • データ操作(本棚に本を置いたり、削除したり、上書きしたりする)
  • データ制御(建物にはいれる人を決めたり、本棚の内容を変えるのを制限したりする)

MySQLコマンド

SQLコマンドに定義されていないものの、各データベースサーバが必要に応じて追加した独自の命令文の集合。それぞれ独自のコマンドなので、データベースサーバごとに大きく異なっている。「MySQLコマンド」という言葉は筆者が名付けただけなので、一般には通じないことに注意。

コマンドを知る前に

ここから各コマンドについて説明するが、その前に知っておくべきこと、用意しておくべきことを説明する。

コマンドの説明の前に知っておくべきこと

データベースサーバ内では、以下の概念を取り扱う。

  • データベース
  • テーブル
  • カラム
  • ユーザー
データベース

「情報を取り扱うための入れ物の入れ物」。本棚の例えで言うところの、管理人が管理しているたくさんの本棚の群れのこと。Excelなら1つのxlsxファイル。Google Spreadsheetsなら1つのスプレッドシート

テーブル

「情報を取り扱うための入れ物」。本棚の例えで言うところの、たくさんの本棚のうちのひとつ。Excelなら1つのxlsxファイルの中の1枚のシート。Google Spreadsheetsなら1つのスプレッドシートの中の1枚のシート。

f:id:mizunokura:20180211162708p:plain

カラム

「入れ物の中の間仕切りと、仕切られた空間につけられた名前」。本棚の例えで言うところの、「書名」や「著者名」といった分類するための情報区分。ExcelGoogle Spreadsheetsなら、たいてい1行目に記載するその列の名前。

f:id:mizunokura:20180211175019p:plain

ユーザー

「操作をする人」。本棚の例えで言うところの、建物にはいる人の名前とパスワード。Excelなら操作している自分自身。Google Spreadsheetsなら、Google にログインするアカウント。

コマンドの説明をする前に本棚を作ろう

コマンドの説明をするにあたって、手元に動作を確認する環境があると理解しやすくなる。MySQL の場合は公式にデータベースとテーブルのセットを用意してくれているので、ダウンロードして使うと良い。

参照: https://dev.mysql.com/doc/index-other.html

ダウンロードしたZIPファイルを解凍して作成された world.sqlmysql クライアントに渡してやると、世界の国々の人口や首都、都市の人口などのデータ、言語などが入ったデータベースを作成してくれる。

$ mysql -u ユーザー名 < ./world.sql

./world.sql の部分は各自ダウンロードしたファイルがある場所までのディレクトリパスを入力する。もしそれが Mac のダウンロードフォルダなら、

$ mysql -u ユーザー名 < ~/Downloads/world.sql

とすることで作成できる。

コマンドを知ろう

ここからコマンドについて説明していく。基本的にエンターキーが押されるまでクライアントは管理人に話しかけに行かない。また、基本的に末尾に ; (セミコロン)や ¥G が付いていなければ、管理人はコマンド入力がまだ続いていると判断して、何もしない((USE など一部コマンドは除く))。以下ではコマンドを大文字表記しているが、小文字でも問題ない。

定義済みのデータベースを一覧取得する(SHOW DATABASES)

データベースサーバ内のデータベース(建物内にある本棚群)の一覧を取ってきてもらい、見るMySQLコマンド。

mysql> SHOW DATABASES;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
| world              |
+--------------------+
5 rows in set (0.00 sec)

ここで表示されたのは、データベースの名前だ。 world 以外の4つは、MySQL データベースサーバインストール時から用意されているデータベース管理用のもの(管理人が使うもの)なので、直接触る必要はない。

操作したいデータベースを選ぶ(USE データベース名)

操作したい本棚の名前を指定するMySQLコマンド。

mysql> use world;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed

本棚を指定しないまま、先に本棚を指定しているべきコマンドを入力した場合、以下のエラーになる。

mysql> show tables;
ERROR 1046 (3D000): No database selected

ログイン時(建物に入る時)に以下のようにすることで、USE データベース名 を入力することなくデータベースを指定することもできる。

$ mysql -u ユーザー名 -h アドレス --port ポート番号 -p データベース名
# または
$ mysql -u ユーザー名 データベース名
# など

定義済みのテーブルを一覧取得する(SHOW TABLES)

データベース内のテーブル(本棚群の中の本棚)の一覧を取ってきてもらい、見るMySQLコマンド。

mysql> SHOW TABLES;
+-----------------+
| Tables_in_world |
+-----------------+
| city            |
| country         |
| countrylanguage |
+-----------------+
3 rows in set (0.00 sec)

定義済みのテーブルひとつを確認する(DESC テーブル名)

データベース内のテーブルひとつの情報を取ってきてもらい、見るMySQLコマンド。ここでは、上で確認した中にある country テーブルの情報を確認しよう。

mysql> DESC country;
+----------------+---------------------------------------------------------------------------------------+------+-----+---------+-------+
| Field          | Type                                                                                  | Null | Key | Default | Extra |
+----------------+---------------------------------------------------------------------------------------+------+-----+---------+-------+
| Code           | char(3)                                                                               | NO   | PRI |         |       |
| Name           | char(52)                                                                              | NO   |     |         |       |
| Continent      | enum('Asia','Europe','North America','Africa','Oceania','Antarctica','South America') | NO   |     | Asia    |       |
| Region         | char(26)                                                                              | NO   |     |         |       |
| SurfaceArea    | float(10,2)                                                                           | NO   |     | 0.00    |       |
| IndepYear      | smallint(6)                                                                           | YES  |     | NULL    |       |
| Population     | int(11)                                                                               | NO   |     | 0       |       |
| LifeExpectancy | float(3,1)                                                                            | YES  |     | NULL    |       |
| GNP            | float(10,2)                                                                           | YES  |     | NULL    |       |
| GNPOld         | float(10,2)                                                                           | YES  |     | NULL    |       |
| LocalName      | char(45)                                                                              | NO   |     |         |       |
| GovernmentForm | char(45)                                                                              | NO   |     |         |       |
| HeadOfState    | char(60)                                                                              | YES  |     | NULL    |       |
| Capital        | int(11)                                                                               | YES  |     | NULL    |       |
| Code2          | char(2)                                                                               | NO   |     |         |       |
+----------------+---------------------------------------------------------------------------------------+------+-----+---------+-------+
15 rows in set (0.01 sec)

Field はカラムに付けられた名前。 Type はカラムに入れられるデータの種類。Null はそのカラムにnull(空のデータ)が入ることを許容するかどうか。Key はそのカラムの一意制約(後述)の情報。Default はそのカラムに値を指定しなかった場合に自動的で入る値。Extra は付随情報。

また Field には、日本語を使うこともできる。

テーブルの中から全カラムの情報を取得する(SELECT * FROM テーブル名)

テーブルの中に定義されている全カラムの情報を取得するコマンド。FROM のあとに続けて、データを取ってきてほしいテーブル名を指定する。ここでは、上で情報を確認した country テーブルの内容を取得してみよう。

mysql> SELECT * FROM country;
+------+----------------------------------------------+---------------+---------------------------+-------------+-----------+------------+----------------+------------+------------+----------------------------------------------+----------------------------------------------+--------------------------------------+---------+-------+
| Code | Name                                         | Continent     | Region                    | SurfaceArea | IndepYear | Population | LifeExpectancy | GNP        | GNPOld     | LocalName                                    | GovernmentForm                               | HeadOfState                          | Capital | Code2 |
+------+----------------------------------------------+---------------+---------------------------+-------------+-----------+------------+----------------+------------+------------+----------------------------------------------+----------------------------------------------+--------------------------------------+---------+-------+
| ABW  | Aruba                                        | North America | Caribbean                 |      193.00 |      NULL |     103000 |           78.4 |     828.00 |     793.00 | Aruba                                        | Nonmetropolitan Territory of The Netherlands | Beatrix                              |     129 | AW    |
| AFG  | Afghanistan                                  | Asia          | Southern and Central Asia |   652090.00 |      1919 |   22720000 |           45.9 |    5976.00 |       NULL | Afganistan/Afqanestan                        | Islamic Emirate                              | Mohammad Omar                        |       1 | AF    |
| AGO  | Angola                                       | Africa        | Central Africa            |  1246700.00 |      1975 |   12878000 |           38.3 |    6648.00 |    7984.00 | Angola                                       | Republic                                     | José Eduardo dos Santos              |      56 | AO    |

(中略)

| ZAF  | South Africa                                 | Africa        | Southern Africa           |  1221037.00 |      1910 |   40377000 |           51.1 |  116729.00 |  129092.00 | South Africa                                 | Republic                                     | Thabo Mbeki                          |     716 | ZA    |
| ZMB  | Zambia                                       | Africa        | Eastern Africa            |   752618.00 |      1964 |    9169000 |           37.2 |    3377.00 |    3922.00 | Zambia                                       | Republic                                     | Frederick Chiluba                    |    3162 | ZM    |
| ZWE  | Zimbabwe                                     | Africa        | Eastern Africa            |   390757.00 |      1980 |   11669000 |           37.8 |    5951.00 |    8670.00 | Zimbabwe                                     | Republic                                     | Robert G. Mugabe                     |    4068 | ZW    |
+------+----------------------------------------------+---------------+---------------------------+-------------+-----------+------------+----------------+------------+------------+----------------------------------------------+----------------------------------------------+--------------------------------------+---------+-------+
239 rows in set (0.01 sec)

239行もあるので、画面が文字で埋まってしまい大変なことになる。そこで、ひとまず名前だけを取ってきてみたい。

テーブルの中から特定のカラムの情報を取得する(SELECT カラム名 FROM テーブル名)

DESC テーブル名 で確認したカラム名Field の部分)を指定することで、そのカラムの情報だけを取ってくることができる。

mysql> SELECT Name FROM country;
+----------------------------------------------+
| Name                                         |
+----------------------------------------------+
| Aruba                                        |
| Afghanistan                                  |
| Angola                                       |

(中略)


| South Africa                                 |
| Zambia                                       |
| Zimbabwe                                     |
+----------------------------------------------+
239 rows in set (0.00 sec)

また、カラム名はカンマ(,)で区切ることで複数指定することもできる。また、全カラム取得時に指定したアスタリスク*)は、じつは「全カラム」という意味を持っている。

mysql> SELECT Code, Name FROM country;
+------+----------------------------------------------+
| Code | Name                                         |
+------+----------------------------------------------+
| ABW  | Aruba                                        |
| AFG  | Afghanistan                                  |
| AGO  | Angola                                       |

(中略)

| ZAF  | South Africa                                 |
| ZMB  | Zambia                                       |
| ZWE  | Zimbabwe                                     |
+------+----------------------------------------------+
239 rows in set (0.00 sec)

これで多少見やすくはなったが、行数が多いのでまだすこしわかりにくいと思う。そこで、今度は指定した件数だけを取ってきてみたい。

テーブルの中から指定件数のデータを取得する(SELECT カラム名 FROM テーブル名 LIMIT 件数)

LIMIT 件数 を指定することで、指定件数のデータだけを取ってくることができる。

mysql> SELECT Code,Name FROM country LIMIT 10;
+------+----------------------+
| Code | Name                 |
+------+----------------------+
| ABW  | Aruba                |
| AFG  | Afghanistan          |
| AGO  | Angola               |
| AIA  | Anguilla             |
| ALB  | Albania              |
| AND  | Andorra              |
| ANT  | Netherlands Antilles |
| ARE  | United Arab Emirates |
| ARG  | Argentina            |
| ARM  | Armenia              |
+------+----------------------+
10 rows in set (0.00 sec)

これで画面が平和になった。しかし、今度は11行目以降が見えなくなってしまった。次の10件を取ってきてもらうには、 OFFSET 件数 を末尾でさらに指定する。

mysql> SELECT Code,Name FROM country LIMIT 10 OFFSET 10;
+------+-----------------------------+
| Code | Name                        |
+------+-----------------------------+
| ASM  | American Samoa              |
| ATA  | Antarctica                  |
| ATF  | French Southern territories |
| ATG  | Antigua and Barbuda         |
| AUS  | Australia                   |
| AUT  | Austria                     |
| AZE  | Azerbaijan                  |
| BDI  | Burundi                     |
| BEL  | Belgium                     |
| BEN  | Benin                       |
+------+-----------------------------+
10 rows in set (0.00 sec)

同様に、次の21行目以降を見るためには OFFSET 20 を指定すれば良い。これで少しずつ見ることができるようなった。では、OFFSET の件数を変えて、日本(Japan)の情報だけを見てみよう。

mysql> SELECT Code,Name FROM country LIMIT 10 OFFSET 100;
+------+--------------------------------+
| Code | Name                           |
+------+--------------------------------+
| IOT  | British Indian Ocean Territory |
| IRL  | Ireland                        |
| IRN  | Iran                           |
| IRQ  | Iraq                           |
| ISL  | Iceland                        |
| ISR  | Israel                         |
| ITA  | Italy                          |
| JAM  | Jamaica                        |
| JOR  | Jordan                         |
| JPN  | Japan                          |
+------+--------------------------------+
10 rows in set (0.00 sec)

…あった。Japan は110行目にようやく見つかった。しかし、今回のように239件しかないテーブルならともかく、たとえば10万件あるテーブルの中から特定のデータ1件を見つけるために、OFFSET 10、20、40…と繰り返すのは手間がかかりすぎる。そこで、日本のように名前がわかっている国のデータを、名前で探せるようにしたい。

テーブルの中から指定条件に完全一致するデータを取得する(SELECT カラム名 FROM テーブル名 WHERE カラム名 = '合致させたい内容')

WHERE に続けて、カラム名と合致させたい内容を指定することで、条件に合うデータ行だけを取得することができる。ここでは国名(Name)が日本(Japan)になっているデータを取ってきてほしいので、WHERE Name = 'Japan' と指定する。

mysql> SELECT * FROM country WHERE Name = 'Japan';
+------+-------+-----------+--------------+-------------+-----------+------------+----------------+------------+------------+--------------+-------------------------+-------------+---------+-------+
| Code | Name  | Continent | Region       | SurfaceArea | IndepYear | Population | LifeExpectancy | GNP        | GNPOld     | LocalName    | GovernmentForm          | HeadOfState | Capital | Code2 |
+------+-------+-----------+--------------+-------------+-----------+------------+----------------+------------+------------+--------------+-------------------------+-------------+---------+-------+
| JPN  | Japan | Asia      | Eastern Asia |   377829.00 |      -660 |  126714000 |           80.7 | 3787042.00 | 4192638.00 | Nihon/Nippon | Constitutional Monarchy | Akihito     |    1532 | JP    |
+------+-------+-----------+--------------+-------------+-----------+------------+----------------+------------+------------+--------------+-------------------------+-------------+---------+-------+
1 row in set (0.00 sec)

無事取ってこれた。しかし横長になってしまってすこし見づらい。その場合は、末尾の ;¥G に変えることで、横並びに表示されているデータを縦並びに表示することができる。

mysql> SELECT * FROM country WHERE Name = 'Japan'\G
*************************** 1. row ***************************
          Code: JPN
          Name: Japan
     Continent: Asia
        Region: Eastern Asia
   SurfaceArea: 377829.00
     IndepYear: -660
    Population: 126714000
LifeExpectancy: 80.7
           GNP: 3787042.00
        GNPOld: 4192638.00
     LocalName: Nihon/Nippon
GovernmentForm: Constitutional Monarchy
   HeadOfState: Akihito
       Capital: 1532
         Code2: JP
1 row in set (0.00 sec)

では、同じように大陸(Continent)がアジア(Asia)になっている国の一覧を取ってきてみよう。

mysql> SELECT * FROM country WHERE Continent = 'Asia'\G

(中略)

*************************** 51. row ***************************
          Code: YEM
          Name: Yemen
     Continent: Asia
        Region: Middle East
   SurfaceArea: 527968.00
     IndepYear: 1918
    Population: 18112000
LifeExpectancy: 59.8
           GNP: 6041.00
        GNPOld: 5729.00
     LocalName: Al-Yaman
GovernmentForm: Republic
   HeadOfState: Ali Abdallah Salih
       Capital: 1780
         Code2: YE
51 rows in set (0.00 sec)

今度は逆に見辛くなってしまったので、横長にしてみる。

mysql> SELECT * FROM country WHERE Continent = 'Asia';
+------+----------------------+-----------+---------------------------+-------------+-----------+------------+----------------+------------+------------+----------------------------------------------+----------------------------------------+----------------------------------+---------+-------+
| Code | Name                 | Continent | Region                    | SurfaceArea | IndepYear | Population | LifeExpectancy | GNP        | GNPOld     | LocalName                                    | GovernmentForm                         | HeadOfState                      | Capital | Code2 |
+------+----------------------+-----------+---------------------------+-------------+-----------+------------+----------------+------------+------------+----------------------------------------------+----------------------------------------+----------------------------------+---------+-------+
| AFG  | Afghanistan          | Asia      | Southern and Central Asia |   652090.00 |      1919 |   22720000 |           45.9 |    5976.00 |       NULL | Afganistan/Afqanestan                        | Islamic Emirate                        | Mohammad Omar                    |       1 | AF    |
| ARE  | United Arab Emirates | Asia      | Middle East               |    83600.00 |      1971 |    2441000 |           74.1 |   37966.00 |   36846.00 | Al-Imarat al-´Arabiya al-Muttahida           | Emirate Federation                     | Zayid bin Sultan al-Nahayan      |      65 | AE    |
| ARM  | Armenia              | Asia      | Middle East               |    29800.00 |      1991 |    3520000 |           66.4 |    1813.00 |    1627.00 | Hajastan                                     | Republic                               | Robert Kotšarjan                 |     126 | AM    |
| AZE  | Azerbaijan           | Asia      | Middle East               |    86600.00 |      1991 |    7734000 |           62.9 |    4127.00 |    4100.00 | Azärbaycan                                   | Federal Republic                       | Heydär Äliyev                    |     144 | AZ    |
| BGD  | Bangladesh           | Asia      | Southern and Central Asia |   143998.00 |      1971 |  129155000 |           60.2 |   32852.00 |   31966.00 | Bangladesh                                   | Republic                               | Shahabuddin Ahmad                |     150 | BD    |
| BHR  | Bahrain              | Asia      | Middle East               |      694.00 |      1971 |     617000 |           73.0 |    6366.00 |    6097.00 | Al-Bahrayn                                   | Monarchy (Emirate)                     | Hamad ibn Isa al-Khalifa         |     149 | BH    |
| BRN  | Brunei               | Asia      | Southeast Asia            |     5765.00 |      1984 |     328000 |           73.6 |   11705.00 |   12460.00 | Brunei Darussalam                            | Monarchy (Sultanate)                   | Haji Hassan al-Bolkiah           |     538 | BN    |
| BTN  | Bhutan               | Asia      | Southern and Central Asia |    47000.00 |      1910 |    2124000 |           52.4 |     372.00 |     383.00 | Druk-Yul                                     | Monarchy                               | Jigme Singye Wangchuk            |     192 | BT    |
| CHN  | China                | Asia      | Eastern Asia              |  9572900.00 |     -1523 | 1277558000 |           71.4 |  982268.00 |  917719.00 | Zhongquo                                     | People'sRepublic                       | Jiang Zemin                      |    1891 | CN    |
| CYP  | Cyprus               | Asia      | Middle East               |     9251.00 |      1960 |     754700 |           76.7 |    9333.00 |    8246.00 | Kýpros/Kibris                                | Republic                               | Glafkos Klerides                 |    2430 | CY    |
| GEO  | Georgia              | Asia      | Middle East               |    69700.00 |      1991 |    4968000 |           64.5 |    6064.00 |    5924.00 | Sakartvelo                                   | Republic                               | Eduard Ševardnadze               |     905 | GE    |
| HKG  | Hong Kong            | Asia      | Eastern Asia              |     1075.00 |      NULL |    6782000 |           79.5 |  166448.00 |  173610.00 | Xianggang/Hong Kong                          | Special Administrative Region of China | Jiang Zemin                      |     937 | HK    |
| IDN  | Indonesia            | Asia      | Southeast Asia            |  1904569.00 |      1945 |  212107000 |           68.0 |   84982.00 |  215002.00 | Indonesia                                    | Republic                               | Abdurrahman Wahid                |     939 | ID    |
| IND  | India                | Asia      | Southern and Central Asia |  3287263.00 |      1947 | 1013662000 |           62.5 |  447114.00 |  430572.00 | Bharat/India                                 | Federal Republic                       | Kocheril Raman Narayanan         |    1109 | IN    |
| IRN  | Iran                 | Asia      | Southern and Central Asia |  1648195.00 |      1906 |   67702000 |           69.7 |  195746.00 |  160151.00 | Iran                                         | Islamic Republic                       | Ali Mohammad Khatami-Ardakani    |    1380 | IR    |
| IRQ  | Iraq                 | Asia      | Middle East               |   438317.00 |      1932 |   23115000 |           66.5 |   11500.00 |       NULL | Al-´Iraq                                     | Republic                               | Saddam Hussein al-Takriti        |    1365 | IQ    |
| ISR  | Israel               | Asia      | Middle East               |    21056.00 |      1948 |    6217000 |           78.6 |   97477.00 |   98577.00 | Yisra’el/Isra’il                             | Republic                               | Moshe Katzav                     |    1450 | IL    |
| JOR  | Jordan               | Asia      | Middle East               |    88946.00 |      1946 |    5083000 |           77.4 |    7526.00 |    7051.00 | Al-Urdunn                                    | Constitutional Monarchy                | Abdullah II                      |    1786 | JO    |
| JPN  | Japan                | Asia      | Eastern Asia              |   377829.00 |      -660 |  126714000 |           80.7 | 3787042.00 | 4192638.00 | Nihon/Nippon                                 | Constitutional Monarchy                | Akihito                          |    1532 | JP    |
| KAZ  | Kazakstan            | Asia      | Southern and Central Asia |  2724900.00 |      1991 |   16223000 |           63.2 |   24375.00 |   23383.00 | Qazaqstan                                    | Republic                               | Nursultan Nazarbajev             |    1864 | KZ    |
| KGZ  | Kyrgyzstan           | Asia      | Southern and Central Asia |   199900.00 |      1991 |    4699000 |           63.4 |    1626.00 |    1767.00 | Kyrgyzstan                                   | Republic                               | Askar Akajev                     |    2253 | KG    |
| KHM  | Cambodia             | Asia      | Southeast Asia            |   181035.00 |      1953 |   11168000 |           56.5 |    5121.00 |    5670.00 | Kâmpuchéa                                    | Constitutional Monarchy                | Norodom Sihanouk                 |    1800 | KH    |
| KOR  | South Korea          | Asia      | Eastern Asia              |    99434.00 |      1948 |   46844000 |           74.4 |  320749.00 |  442544.00 | Taehan Min’guk (Namhan)                      | Republic                               | Kim Dae-jung                     |    2331 | KR    |
| KWT  | Kuwait               | Asia      | Middle East               |    17818.00 |      1961 |    1972000 |           76.1 |   27037.00 |   30373.00 | Al-Kuwayt                                    | Constitutional Monarchy (Emirate)      | Jabir al-Ahmad al-Jabir al-Sabah |    2429 | KW    |
| LAO  | Laos                 | Asia      | Southeast Asia            |   236800.00 |      1953 |    5433000 |           53.1 |    1292.00 |    1746.00 | Lao                                          | Republic                               | Khamtay Siphandone               |    2432 | LA    |
| LBN  | Lebanon              | Asia      | Middle East               |    10400.00 |      1941 |    3282000 |           71.3 |   17121.00 |   15129.00 | Lubnan                                       | Republic                               | Émile Lahoud                     |    2438 | LB    |
| LKA  | Sri Lanka            | Asia      | Southern and Central Asia |    65610.00 |      1948 |   18827000 |           71.8 |   15706.00 |   15091.00 | Sri Lanka/Ilankai                            | Republic                               | Chandrika Kumaratunga            |    3217 | LK    |
| MAC  | Macao                | Asia      | Eastern Asia              |       18.00 |      NULL |     473000 |           81.6 |    5749.00 |    5940.00 | Macau/Aomen                                  | Special Administrative Region of China | Jiang Zemin                      |    2454 | MO    |
| MDV  | Maldives             | Asia      | Southern and Central Asia |      298.00 |      1965 |     286000 |           62.2 |     199.00 |       NULL | Dhivehi Raajje/Maldives                      | Republic                               | Maumoon Abdul Gayoom             |    2463 | MV    |
| MMR  | Myanmar              | Asia      | Southeast Asia            |   676578.00 |      1948 |   45611000 |           54.9 |  180375.00 |  171028.00 | Myanma Pye                                   | Republic                               | kenraali Than Shwe               |    2710 | MM    |
| MNG  | Mongolia             | Asia      | Eastern Asia              |  1566500.00 |      1921 |    2662000 |           67.3 |    1043.00 |     933.00 | Mongol Uls                                   | Republic                               | Natsagiin Bagabandi              |    2696 | MN    |
| MYS  | Malaysia             | Asia      | Southeast Asia            |   329758.00 |      1957 |   22244000 |           70.8 |   69213.00 |   97884.00 | Malaysia                                     | Constitutional Monarchy, Federation    | Salahuddin Abdul Aziz Shah Alhaj |    2464 | MY    |
| NPL  | Nepal                | Asia      | Southern and Central Asia |   147181.00 |      1769 |   23930000 |           57.8 |    4768.00 |    4837.00 | Nepal                                        | Constitutional Monarchy                | Gyanendra Bir Bikram             |    2729 | NP    |
| OMN  | Oman                 | Asia      | Middle East               |   309500.00 |      1951 |    2542000 |           71.8 |   16904.00 |   16153.00 | ´Uman                                        | Monarchy (Sultanate)                   | Qabus ibn Sa´id                  |    2821 | OM    |
| PAK  | Pakistan             | Asia      | Southern and Central Asia |   796095.00 |      1947 |  156483000 |           61.1 |   61289.00 |   58549.00 | Pakistan                                     | Republic                               | Mohammad Rafiq Tarar             |    2831 | PK    |
| PHL  | Philippines          | Asia      | Southeast Asia            |   300000.00 |      1946 |   75967000 |           67.5 |   65107.00 |   82239.00 | Pilipinas                                    | Republic                               | Gloria Macapagal-Arroyo          |     766 | PH    |
| PRK  | North Korea          | Asia      | Eastern Asia              |   120538.00 |      1948 |   24039000 |           70.7 |    5332.00 |       NULL | Choson Minjujuui In´min Konghwaguk (Bukhan)  | Socialistic Republic                   | Kim Jong-il                      |    2318 | KP    |
| PSE  | Palestine            | Asia      | Middle East               |     6257.00 |      NULL |    3101000 |           71.4 |    4173.00 |       NULL | Filastin                                     | Autonomous Area                        | Yasser (Yasir) Arafat            |    4074 | PS    |
| QAT  | Qatar                | Asia      | Middle East               |    11000.00 |      1971 |     599000 |           72.4 |    9472.00 |    8920.00 | Qatar                                        | Monarchy                               | Hamad ibn Khalifa al-Thani       |    2973 | QA    |
| SAU  | Saudi Arabia         | Asia      | Middle East               |  2149690.00 |      1932 |   21607000 |           67.8 |  137635.00 |  146171.00 | Al-´Arabiya as-Sa´udiya                      | Monarchy                               | Fahd ibn Abdul-Aziz al-Sa´ud     |    3173 | SA    |
| SGP  | Singapore            | Asia      | Southeast Asia            |      618.00 |      1965 |    3567000 |           80.1 |   86503.00 |   96318.00 | Singapore/Singapura/Xinjiapo/Singapur        | Republic                               | Sellapan Rama Nathan             |    3208 | SG    |
| SYR  | Syria                | Asia      | Middle East               |   185180.00 |      1941 |   16125000 |           68.5 |   65984.00 |   64926.00 | Suriya                                       | Republic                               | Bashar al-Assad                  |    3250 | SY    |
| THA  | Thailand             | Asia      | Southeast Asia            |   513115.00 |      1350 |   61399000 |           68.6 |  116416.00 |  153907.00 | Prathet Thai                                 | Constitutional Monarchy                | Bhumibol Adulyadej               |    3320 | TH    |
| TJK  | Tajikistan           | Asia      | Southern and Central Asia |   143100.00 |      1991 |    6188000 |           64.1 |    1990.00 |    1056.00 | Toçikiston                                   | Republic                               | Emomali Rahmonov                 |    3261 | TJ    |
| TKM  | Turkmenistan         | Asia      | Southern and Central Asia |   488100.00 |      1991 |    4459000 |           60.9 |    4397.00 |    2000.00 | Türkmenostan                                 | Republic                               | Saparmurad Nijazov               |    3419 | TM    |
| TMP  | East Timor           | Asia      | Southeast Asia            |    14874.00 |      NULL |     885000 |           46.0 |       0.00 |       NULL | Timor Timur                                  | Administrated by the UN                | José Alexandre Gusmão            |    1522 | TP    |
| TUR  | Turkey               | Asia      | Middle East               |   774815.00 |      1923 |   66591000 |           71.0 |  210721.00 |  189122.00 | Türkiye                                      | Republic                               | Ahmet Necdet Sezer               |    3358 | TR    |
| TWN  | Taiwan               | Asia      | Eastern Asia              |    36188.00 |      1945 |   22256000 |           76.4 |  256254.00 |  263451.00 | T’ai-wan                                     | Republic                               | Chen Shui-bian                   |    3263 | TW    |
| UZB  | Uzbekistan           | Asia      | Southern and Central Asia |   447400.00 |      1991 |   24318000 |           63.7 |   14194.00 |   21300.00 | Uzbekiston                                   | Republic                               | Islam Karimov                    |    3503 | UZ    |
| VNM  | Vietnam              | Asia      | Southeast Asia            |   331689.00 |      1945 |   79832000 |           69.3 |   21929.00 |   22834.00 | Viêt Nam                                     | Socialistic Republic                   | Trân Duc Luong                   |    3770 | VN    |
| YEM  | Yemen                | Asia      | Middle East               |   527968.00 |      1918 |   18112000 |           59.8 |    6041.00 |    5729.00 | Al-Yaman                                     | Republic                               | Ali Abdallah Salih               |    1780 | YE    |
+------+----------------------+-----------+---------------------------+-------------+-----------+------------+----------------+------------+------------+----------------------------------------------+----------------------------------------+----------------------------------+---------+-------+
51 rows in set (0.00 sec)

このように、 = で指定した内容に完全一致する行だけが取得できた。では、世界の中で、日本と同じように国名の先頭が 'J' で始まる国はいくつあるだろうか。

テーブルの中から指定条件に部分一致するデータを取得する(SELECT カラム名 FROM テーブル名 WHERE カラム名 LIKE '合致させたい内容')

=の代わりにLIKEを使うことで、完全一致ではなく部分一致でデータを探すことができる。指定したい箇所以外の文字はパーセント(%)で表記する。たとえば「国名がJではじまる」であれば WHERE Name LIKE 'J%' とし、「国名がnで終わる」であれば WHERE Name LIKE '%n' とし、「国名の先頭と末尾以外がapa」であれば、 WHERE Name Like '%apa%' と指定する。

mysql> SELECT * FROM country WHERE Name LIKE 'J%';
+------+---------+---------------+--------------+-------------+-----------+------------+----------------+------------+------------+--------------+-------------------------+--------------+---------+-------+
| Code | Name    | Continent     | Region       | SurfaceArea | IndepYear | Population | LifeExpectancy | GNP        | GNPOld     | LocalName    | GovernmentForm          | HeadOfState  | Capital | Code2 |
+------+---------+---------------+--------------+-------------+-----------+------------+----------------+------------+------------+--------------+-------------------------+--------------+---------+-------+
| JAM  | Jamaica | North America | Caribbean    |    10990.00 |      1962 |    2583000 |           75.2 |    6871.00 |    6722.00 | Jamaica      | Constitutional Monarchy | Elisabeth II |    1530 | JM    |
| JOR  | Jordan  | Asia          | Middle East  |    88946.00 |      1946 |    5083000 |           77.4 |    7526.00 |    7051.00 | Al-Urdunn    | Constitutional Monarchy | Abdullah II  |    1786 | JO    |
| JPN  | Japan   | Asia          | Eastern Asia |   377829.00 |      -660 |  126714000 |           80.7 | 3787042.00 | 4192638.00 | Nihon/Nippon | Constitutional Monarchy | Akihito      |    1532 | JP    |
+------+---------+---------------+--------------+-------------+-----------+------------+----------------+------------+------------+--------------+-------------------------+--------------+---------+-------+
3 rows in set (0.00 sec)

J で始まる国は、ジャマイカとヨルダンと日本だけだった。では、同じように国名が 'K' で始まり、かつアジア大陸にある国だけを取ってきてほしい場合はどうすればいいだろう。その場合は、 WHERE Continent = 'Asia'WHERE Name LIKE 'K%' のふたつの条件を組み合わせなければならない。

テーブルの中から複数条件のすべてに一致するデータを取得する(SELECT カラム名 FROM テーブル名 WHERE 条件 AND 条件)

複数条件を指定したい場合、WHERE のあとにふたつ以上の条件を AND でつなげることで指定することができる。WHERE Continent = 'Asia'WHERE Name LIKE 'K%' なら、WHERE Continent = 'Asia' AND Name LIKE 'K%'WHERE はひとつで良い)になる。

mysql> SELECT * FROM country WHERE Name LIKE 'K%' AND continent = 'Asia';
+------+------------+-----------+---------------------------+-------------+-----------+------------+----------------+----------+----------+------------+-----------------------------------+----------------------------------+---------+-------+
| Code | Name       | Continent | Region                    | SurfaceArea | IndepYear | Population | LifeExpectancy | GNP      | GNPOld   | LocalName  | GovernmentForm                    | HeadOfState                      | Capital | Code2 |
+------+------------+-----------+---------------------------+-------------+-----------+------------+----------------+----------+----------+------------+-----------------------------------+----------------------------------+---------+-------+
| KAZ  | Kazakstan  | Asia      | Southern and Central Asia |  2724900.00 |      1991 |   16223000 |           63.2 | 24375.00 | 23383.00 | Qazaqstan  | Republic                          | Nursultan Nazarbajev             |    1864 | KZ    |
| KGZ  | Kyrgyzstan | Asia      | Southern and Central Asia |   199900.00 |      1991 |    4699000 |           63.4 |  1626.00 |  1767.00 | Kyrgyzstan | Republic                          | Askar Akajev                     |    2253 | KG    |
| KWT  | Kuwait     | Asia      | Middle East               |    17818.00 |      1961 |    1972000 |           76.1 | 27037.00 | 30373.00 | Al-Kuwayt  | Constitutional Monarchy (Emirate) | Jabir al-Ahmad al-Jabir al-Sabah |    2429 | KW    |
+------+------------+-----------+---------------------------+-------------+-----------+------------+----------------+----------+----------+------------+-----------------------------------+----------------------------------+---------+-------+
3 rows in set (0.00 sec)

カザフスタンキルギスクウェートが条件に合致した。では、国名の先頭が 'K' もしくは 'P' の国ではどうだろうか。

テーブルの中から複数条件のいずれかまたは両方に一致するデータを取得する(SELECT カラム名 FROM テーブル名 WHERE 条件 OR 条件)

いずれかまたは両方に合致させたい複数条件を指定する場合、WHERE のあとにふたつ以上の条件を OR でつなげることで指定することができる。WHERE Name LIKE 'P%'WHERE Name LIKE 'K%' なら、WHERE Name LIKE 'P%' OR Name LIKE 'K%'WHERE はひとつで良い)になる。

mysql> SELECT Name, Continent FROM country WHERE Name LIKE 'P%' OR Name LIKE 'K%';
+------------------+---------------+
| Name             | Continent     |
+------------------+---------------+
| Kazakstan        | Asia          |
| Kenya            | Africa        |
| Kyrgyzstan       | Asia          |
| Kiribati         | Oceania       |
| Kuwait           | Asia          |
| Pakistan         | Asia          |
| Panama           | North America |
| Pitcairn         | Oceania       |
| Peru             | South America |
| Philippines      | Asia          |
| Palau            | Oceania       |
| Papua New Guinea | Oceania       |
| Poland           | Europe        |
| Puerto Rico      | North America |
| Portugal         | Europe        |
| Paraguay         | South America |
| Palestine        | Asia          |
+------------------+---------------+
17 rows in set (0.00 sec)

無事取得することができた。さらにアジア大陸の国だけに絞る場合、 Continent = 'Asia' を条件に追加する必要がある。大陸の指定は、Pで始まる国でもKで始まる国でも絞り込みたい条件なので、 OR で追加することはできない。そのまま AND でつなげた場合、以下のようになる。

mysql> SELECT Name, Continent FROM country WHERE Name LIKE 'P%' OR Name LIKE 'K%' AND Continent = 'Asia';
+------------------+---------------+
| Name             | Continent     |
+------------------+---------------+
| Kazakstan        | Asia          |
| Kyrgyzstan       | Asia          |
| Kuwait           | Asia          |
| Pakistan         | Asia          |
| Panama           | North America |
| Pitcairn         | Oceania       |
| Peru             | South America |
| Philippines      | Asia          |
| Palau            | Oceania       |
| Papua New Guinea | Oceania       |
| Poland           | Europe        |
| Puerto Rico      | North America |
| Portugal         | Europe        |
| Paraguay         | South America |
| Palestine        | Asia          |
+------------------+---------------+
15 rows in set (0.00 sec)

Kで始まる国についてはアジア大陸だけに絞り込まれているが、Pで始まる国については絞り込めていない。これは、 WHERE Name LIKE 'P%' OR Name LIKE 'K%' AND Continent = 'Asia' という条件が、「国名がPで始まる」もしくは「国名がKで始まり、かつ大陸がアジア」という意味になってしまっているからだ。これを「国名がPで始まる」または「国名がKで始まる」、かつ「大陸がアジア」としたい場合、またはの条件のほうをカッコで囲み、WHERE ( Name LIKE 'P%' OR Name LIKE 'K%' ) AND Continent = 'Asia' のようにする。

mysql> SELECT Name, Continent FROM country WHERE ( Name LIKE 'P%' OR Name LIKE 'K%' ) AND Continent = 'Asia';
+-------------+-----------+
| Name        | Continent |
+-------------+-----------+
| Kazakstan   | Asia      |
| Kyrgyzstan  | Asia      |
| Kuwait      | Asia      |
| Pakistan    | Asia      |
| Philippines | Asia      |
| Palestine   | Asia      |
+-------------+-----------+
6 rows in set (0.00 sec)

無事絞り込めた。ところで国名がアルファベット順になっていないので、これを並び替えたい。

テーブルの取得結果をカラムの内容で並び替える(SELECT カラム名 FROM テーブル名 ORDER BY カラム名 昇順または降順)

SELECT での取得結果を並び替えて表示したい場合、 ORDER BY カラム名 に続けて、昇順(ASC)または降順(DESC)を指定すれば良い(指定しなかった場合昇順になる)。

mysql> SELECT Name, Continent FROM country WHERE ( Name LIKE 'P%' OR Name LIKE 'K%' ) AND Continent = 'Asia' ORDER BY Name ASC;
+-------------+-----------+
| Name        | Continent |
+-------------+-----------+
| Kazakstan   | Asia      |
| Kuwait      | Asia      |
| Kyrgyzstan  | Asia      |
| Pakistan    | Asia      |
| Palestine   | Asia      |
| Philippines | Asia      |
+-------------+-----------+
6 rows in set (0.00 sec)

アルファベットの昇順になった。これを応用して、たとえば世界の面積の大きな国のランキングを見ることもできる。

mysql> SELECT Name, SurfaceArea FROM country ORDER BY SurfaceArea DESC LIMIT 10;
+--------------------+-------------+
| Name               | SurfaceArea |
+--------------------+-------------+
| Russian Federation | 17075400.00 |
| Antarctica         | 13120000.00 |
| Canada             |  9970610.00 |
| China              |  9572900.00 |
| United States      |  9363520.00 |
| Brazil             |  8547403.00 |
| Australia          |  7741220.00 |
| India              |  3287263.00 |
| Argentina          |  2780400.00 |
| Kazakstan          |  2724900.00 |
+--------------------+-------------+
10 rows in set (0.00 sec)

テーブルの取得条件に以上、以下を使う(SELECT カラム名 FROM テーブル名 WHERE カラム名 >= 数値 OR カラム名 <= 数値)

>=,<=,>,< を使うことで、以上、以下、より大きい、未満、を指定することができる。たとえば平均余命(LifeExpectancy)が80歳以上の国を取ってくる場合、 WHERE LifeExpectancy >= 80 のように指定する。

mysql> SELECT Name, LifeExpectancy FROM country WHERE LifeExpectancy >= 80;
+------------+----------------+
| Name       | LifeExpectancy |
+------------+----------------+
| Andorra    |           83.5 |
| Japan      |           80.7 |
| Macao      |           81.6 |
| Singapore  |           80.1 |
| San Marino |           81.1 |
+------------+----------------+
5 rows in set (0.00 sec)

逆に平均余命が40歳以下の国は、以下のように取ってくることができる。

mysql> SELECT Name, LifeExpectancy FROM country WHERE LifeExpectancy <= 40;
+------------+----------------+
| Name       | LifeExpectancy |
+------------+----------------+
| Angola     |           38.3 |
| Botswana   |           39.3 |
| Mozambique |           37.5 |
| Malawi     |           37.6 |
| Rwanda     |           39.3 |
| Zambia     |           37.2 |
| Zimbabwe   |           37.8 |
+------------+----------------+
7 rows in set (0.00 sec)

テーブルの全件数を取得する(SELECT COUNT(*) FROM テーブル名)

特定のテーブルで管理されているデータの全件数が取得したい場合、 SELECT * FROM テーブル名 で全件取得することで確認することもできるが、取得対象カラム名COUNT(*) を指定することで、もっとスマートに件数だけを取ってきてもらうこともできる。

mysql> SELECT COUNT(*) FROM country;
+----------+
| COUNT(*) |
+----------+
|      239 |
+----------+
1 row in set (0.00 sec)

COUNT(*)COUNT(1) と表記しても良い。

mysql> SELECT COUNT(1) FROM country;
+----------+
| COUNT(1) |
+----------+
|      239 |
+----------+
1 row in set (0.03 sec)

では、大陸別の国の数を取ってきてみたい。大陸(Continent)でまとめることができれば、数えることができそうだ。

テーブルの内容を特定のカラムの内容でグループ分けする(SELECT * FROM テーブル名 GROUP BY カラム名

GROUP BY カラム名 を指定することで、指定したカラムの内容でまとめた結果を取得することができる。これは主に COUNT() などの複数のカラムの内容をまとめるコマンドと併用する。併用しなかった場合、以下のようにまとめられたうちの1件だけが表示される。

mysql> SELECT Name, Continent FROM country GROUP BY Continent;
+----------------+---------------+
| Name           | Continent     |
+----------------+---------------+
| Afghanistan    | Asia          |
| Albania        | Europe        |
| Aruba          | North America |
| Angola         | Africa        |
| American Samoa | Oceania       |
| Antarctica     | Antarctica    |
| Argentina      | South America |
+----------------+---------------+
7 rows in set (0.00 sec)

ここで COUNT(*) を併用すると、以下のようになる。

mysql> SELECT COUNT(1), Continent FROM country GROUP BY Continent;
+----------+---------------+
| COUNT(1) | Continent     |
+----------+---------------+
|       51 | Asia          |
|       46 | Europe        |
|       37 | North America |
|       58 | Africa        |
|       28 | Oceania       |
|        5 | Antarctica    |
|       14 | South America |
+----------+---------------+
7 rows in set (0.00 sec)

アフリカ大陸の国が一番多いことがわかった。COUNT() と同じように、GROUP BY と組み合わせて取得結果を操作するコマンドは他にもある。

指定されたカラムの最大値、最小値を取得する(SELECT MAX(カラム名), MIN(カラム名) FROM テーブル名 GROUP BY カラム名

MAX()MIN() を使うことで、最大値と最小値を取得することができる(MAX()MIN()はもちろん個別に使うこともできる)。

mysql> SELECT MAX(SurfaceArea), MIN(SurfaceArea), Continent FROM country GROUP BY Continent;
+------------------+------------------+---------------+
| MAX(SurfaceArea) | MIN(SurfaceArea) | Continent     |
+------------------+------------------+---------------+
|       9572900.00 |            18.00 | Asia          |
|      17075400.00 |             0.40 | Europe        |
|       9970610.00 |            53.00 | North America |
|       2505813.00 |            78.00 | Africa        |
|       7741220.00 |            12.00 | Oceania       |
|      13120000.00 |            59.00 | Antarctica    |
|       8547403.00 |         12173.00 | South America |
+------------------+------------------+---------------+
7 rows in set (0.00 sec)

アジア最大の国は9572900.00平方km、最小の国は18.00平方kmであることがわかった。(それぞれ = で指定して国名を調べてみても面白いかもしれない)

指定されたカラムに登録されている内容の合計値を取得する(SELECT SUM(カラム名) FROM テーブル名)

SUM(カラム名) を使うことで、合計値が取得できる。

mysql> SELECT SUM(SurfaceArea),  Continent FROM country GROUP BY Continent;
+------------------+---------------+
| SUM(SurfaceArea) | Continent     |
+------------------+---------------+
|      31881005.00 | Asia          |
|      23049133.90 | Europe        |
|      24214470.00 | North America |
|      30250377.00 | Africa        |
|       8564294.00 | Oceania       |
|      13132101.00 | Antarctica    |
|      17864926.00 | South America |
+------------------+---------------+
7 rows in set (0.01 sec)

各大陸の国の面積の合計値(=各大陸の面積?)が取得できた。

指定されたカラムに登録されている内容の文字数を取得する(SELECT LENGTH(カラム名) FROM テーブル名)

LENGTH(カラム名) を指定することで、その内容の文字数を取得することができる。SUM() と組み合わせて、以下のようなこともできる。

mysql> SELECT SUM(LENGTH(Name)),  Continent FROM country GROUP BY Continent;
+-------------------+---------------+
| SUM(LENGTH(Name)) | Continent     |
+-------------------+---------------+
|               396 | Asia          |
|               425 | Europe        |
|               446 | North America |
|               547 | Africa        |
|               359 | Oceania       |
|               127 | Antarctica    |
|               113 | South America |
+-------------------+---------------+
7 rows in set (0.01 sec)

各大陸の国名の英語表記名の文字数合計が取得できた。(国名が20文字以上の国の一覧や、5文字以下の国一覧を取ってきても面白いかもしれない)

指定されたカラムに登録されている内容を重複を取り除いて表示する(SELECT DISTINCT カラム名 FROM テーブル名)

DISTINCT カラム名 を使うことで、重複を取り除いた結果を取得することができる。

mysql> SELECT DISTINCT Continent FROM country;
+---------------+
| Continent     |
+---------------+
| North America |
| Asia          |
| Africa        |
| Europe        |
| South America |
| Oceania       |
| Antarctica    |
+---------------+
7 rows in set (0.00 sec)

大陸名(Continent)を指定することで、その内容を重複なしで取ってこれた。

複数のカラムの内容や文字列を結合して結果に含める(SELECT CONCAT(文字列やカラム名, 文字列やカラム名, ...) FROM テーブル名)

CONCAT() を使うことで、複数の文字の結合をすることができる。'Hello!' ' ' 'World!' の3つの文字列を組み合わせるには、以下のようにする。

mysql> SELECT CONCAT('Hello!', ' ', 'World!');
+---------------------------------+
| CONCAT('Hello!', ' ', 'World!') |
+---------------------------------+
| Hello! World!                   |
+---------------------------------+
1 row in set (0.01 sec)

文字列の代わりにカラム名を指定することもできる。たとえば国名と大陸名をくっつけて表示したい場合、以下のようにする。

mysql> SELECT CONCAT(Name, ' (', Continent, ')') FROM country LIMIT 10;
+--------------------------------------+
| CONCAT(Name, ' (', Continent, ')')   |
+--------------------------------------+
| Aruba (North America)                |
| Afghanistan (Asia)                   |
| Angola (Africa)                      |
| Anguilla (North America)             |
| Albania (Europe)                     |
| Andorra (Europe)                     |
| Netherlands Antilles (North America) |
| United Arab Emirates (Asia)          |
| Argentina (South America)            |
| Armenia (Asia)                       |
+--------------------------------------+
10 rows in set (0.00 sec)

CONCAT(Name, ' (', Continent, ')') の表示が少し見づらいので、これに別名をつけてみたい。

取得結果に別名をつける(SELECT 取得結果 AS 別名)

AS 別名 を追加することで、別名をつけることができる。

mysql> SELECT CONCAT(Name, ' (', Continent, ')') AS NameContinent FROM country LIMIT 10;
+--------------------------------------+
| NameContinent                        |
+--------------------------------------+
| Aruba (North America)                |
| Afghanistan (Asia)                   |
| Angola (Africa)                      |
| Anguilla (North America)             |
| Albania (Europe)                     |
| Andorra (Europe)                     |
| Netherlands Antilles (North America) |
| United Arab Emirates (Asia)          |
| Argentina (South America)            |
| Armenia (Asia)                       |
+--------------------------------------+
10 rows in set (0.00 sec)

この別名は、並び替えのカラム名にも指定することができる。

mysql> SELECT CONCAT(Name, ' (', Continent, ')') AS NameContinent FROM country ORDER BY NameContinent LIMIT 10;
+-------------------------------------+
| NameContinent                       |
+-------------------------------------+
| Afghanistan (Asia)                  |
| Albania (Europe)                    |
| Algeria (Africa)                    |
| American Samoa (Oceania)            |
| Andorra (Europe)                    |
| Angola (Africa)                     |
| Anguilla (North America)            |
| Antarctica (Antarctica)             |
| Antigua and Barbuda (North America) |
| Argentina (South America)           |
+-------------------------------------+
10 rows in set (0.01 sec)

ふたつのテーブルを組み合わせて結果を取得する

さて、ここまで一通りの取得操作を見てきた。これで、ひとつのテーブルからデータを取ってくることは、一通りできるようになったと思う。では、ここからはふたつのテーブルの内容を組み合わせて表示する方法を説明していく。すこし複雑になるので、わからなくなったら、無理せずまたここに戻ってきてほしい。

いままで country テーブルを使って説明してきたが、 world データベースの中には他にもテーブルがある。

mysql> SHOW TABLES;
+-----------------+
| Tables_in_world |
+-----------------+
| city            |
| country         |
| countrylanguage |
+-----------------+
3 rows in set (0.00 sec)

city を見てみよう。

mysql> DESC city;
+-------------+----------+------+-----+---------+----------------+
| Field       | Type     | Null | Key | Default | Extra          |
+-------------+----------+------+-----+---------+----------------+
| ID          | int(11)  | NO   | PRI | NULL    | auto_increment |
| Name        | char(35) | NO   |     |         |                |
| CountryCode | char(3)  | NO   | MUL |         |                |
| District    | char(20) | NO   |     |         |                |
| Population  | int(11)  | NO   |     | 0       |                |
+-------------+----------+------+-----+---------+----------------+
5 rows in set (0.03 sec)

ID番号、名前、国コード、県名、人口が登録されているようだ。では、試しに都市名(Name)が 'Tripoli'トリポリ)になっている都市を探してみよう。

mysql> select * from city where Name = 'Tripoli';
+------+---------+-------------+-----------+------------+
| ID   | Name    | CountryCode | District  | Population |
+------+---------+-------------+-----------+------------+
| 2439 | Tripoli | LBN         | al-Shamal |     240000 |
| 2441 | Tripoli | LBY         | Tripoli   |    1682000 |
+------+---------+-------------+-----------+------------+
2 rows in set (0.00 sec)

該当する都市が2件見つかった。それぞれ CountryCode が LBN, LBY となっているが、これだけでは国名がはっきりしない*6。国コードを使って、以下のように個別に調べてやることもできる。

mysql> select * from country WHERE Code = 'LBN';
+------+---------+-----------+-------------+-------------+-----------+------------+----------------+----------+----------+-----------+----------------+---------------+---------+-------+
| Code | Name    | Continent | Region      | SurfaceArea | IndepYear | Population | LifeExpectancy | GNP      | GNPOld   | LocalName | GovernmentForm | HeadOfState   | Capital | Code2 |
+------+---------+-----------+-------------+-------------+-----------+------------+----------------+----------+----------+-----------+----------------+---------------+---------+-------+
| LBN  | Lebanon | Asia      | Middle East |    10400.00 |      1941 |    3282000 |           71.3 | 17121.00 | 15129.00 | Lubnan    | Republic       | Émile Lahoud  |    2438 | LB    |
+------+---------+-----------+-------------+-------------+-----------+------------+----------------+----------+----------+-----------+----------------+---------------+---------+-------+
1 row in set (0.00 sec)
ふたつのテーブルに該当する行を結合して取得する(INNER JOIN)

LBN はレバノンだとわかった。しかし、こうやっていちいち該当するデータを別のテーブルから検索してくるのは手間がかかりすぎる。そこで、citycountry の内容を結合して表示したい。結合するには、 INNER JOIN を使用する。city テーブルに対して country テーブルを結合するので、 SELECT * FROM city INNER JOIN country WHERE Name = 'Tripoli'; とする。これを実行すると以下のようになる。

mysql> SELECT * FROM city INNER JOIN country WHERE Name = 'Tripoli';
ERROR 1052 (23000): Column 'Name' in where clause is ambiguous

エラーメッセージの中の ambiguous とは「曖昧な」という意味で、エラーメッセージ全体では「WHERE の中に書いてある 'Name' というカラム名は曖昧だ」と言われている。どういうことだろうか。それぞれのテーブルをよく見てみよう。

mysql> DESC city;
+-------------+----------+------+-----+---------+----------------+
| Field       | Type     | Null | Key | Default | Extra          |
+-------------+----------+------+-----+---------+----------------+
| ID          | int(11)  | NO   | PRI | NULL    | auto_increment |
| Name        | char(35) | NO   |     |         |                |
| CountryCode | char(3)  | NO   | MUL |         |                |
| District    | char(20) | NO   |     |         |                |
| Population  | int(11)  | NO   |     | 0       |                |
+-------------+----------+------+-----+---------+----------------+
5 rows in set (0.01 sec)

mysql> DESC country;
+----------------+---------------------------------------------------------------------------------------+------+-----+---------+-------+
| Field          | Type                                                                                  | Null | Key | Default | Extra |
+----------------+---------------------------------------------------------------------------------------+------+-----+---------+-------+
| Code           | char(3)                                                                               | NO   | PRI |         |       |
| Name           | char(52)                                                                              | NO   |     |         |       |
| Continent      | enum('Asia','Europe','North America','Africa','Oceania','Antarctica','South America') | NO   |     | Asia    |       |
| Region         | char(26)                                                                              | NO   |     |         |       |
| SurfaceArea    | float(10,2)                                                                           | NO   |     | 0.00    |       |
| IndepYear      | smallint(6)                                                                           | YES  |     | NULL    |       |
| Population     | int(11)                                                                               | NO   |     | 0       |       |
| LifeExpectancy | float(3,1)                                                                            | YES  |     | NULL    |       |
| GNP            | float(10,2)                                                                           | YES  |     | NULL    |       |
| GNPOld         | float(10,2)                                                                           | YES  |     | NULL    |       |
| LocalName      | char(45)                                                                              | NO   |     |         |       |
| GovernmentForm | char(45)                                                                              | NO   |     |         |       |
| HeadOfState    | char(60)                                                                              | YES  |     | NULL    |       |
| Capital        | int(11)                                                                               | YES  |     | NULL    |       |
| Code2          | char(2)                                                                               | NO   |     |         |       |
+----------------+---------------------------------------------------------------------------------------+------+-----+---------+-------+
15 rows in set (0.00 sec)

Name カラムに注目しよう。両方のテーブルに 'Name' カラムがあることがわかるはずだ。「曖昧だ」というのはつまり、「city テーブルの中の Name カラムなのか、country テーブルの中の Name カラムなのかが曖昧だ」という意味なのだ。どちらのテーブルの Name カラムなのか、はっきりと指定してやる必要がある。指定したいのは city テーブルなので、city.Name のように書くことができる。SELECT * FROM city INNER JOIN country WHERE city.Name = 'Tripoli'; これを実行すると、以下のようになる。

mysql> SELECT * FROM city INNER JOIN country WHERE city.Name = 'Tripoli';
+------+---------+-------------+-----------+------------+------+----------------------------------------------+---------------+---------------------------+-------------+-----------+------------+----------------+------------+------------+----------------------------------------------+----------------------------------------------+--------------------------------------+---------+-------+
| ID   | Name    | CountryCode | District  | Population | Code | Name                                         | Continent     | Region                    | SurfaceArea | IndepYear | Population | LifeExpectancy | GNP        | GNPOld     | LocalName                                    | GovernmentForm                               | HeadOfState                          | Capital | Code2 |
+------+---------+-------------+-----------+------------+------+----------------------------------------------+---------------+---------------------------+-------------+-----------+------------+----------------+------------+------------+----------------------------------------------+----------------------------------------------+--------------------------------------+---------+-------+
| 2439 | Tripoli | LBN         | al-Shamal |     240000 | ABW  | Aruba                                        | North America | Caribbean                 |      193.00 |      NULL |     103000 |           78.4 |     828.00 |     793.00 | Aruba                                        | Nonmetropolitan Territory of The Netherlands | Beatrix                              |     129 | AW    |
| 2439 | Tripoli | LBN         | al-Shamal |     240000 | AFG  | Afghanistan                                  | Asia          | Southern and Central Asia |   652090.00 |      1919 |   22720000 |           45.9 |    5976.00 |       NULL | Afganistan/Afqanestan                        | Islamic Emirate                              | Mohammad Omar                        |       1 | AF    |
| 2439 | Tripoli | LBN         | al-Shamal |     240000 | AGO  | Angola                                       | Africa        | Central Africa            |  1246700.00 |      1975 |   12878000 |           38.3 |    6648.00 |    7984.00 | Angola                                       | Republic                                     | José Eduardo dos Santos              |      56 | AO    |

(中略)

| 2441 | Tripoli | LBY         | Tripoli   |    1682000 | ZAF  | South Africa                                 | Africa        | Southern Africa           |  1221037.00 |      1910 |   40377000 |           51.1 |  116729.00 |  129092.00 | South Africa                                 | Republic                                     | Thabo Mbeki                          |     716 | ZA    |
| 2441 | Tripoli | LBY         | Tripoli   |    1682000 | ZMB  | Zambia                                       | Africa        | Eastern Africa            |   752618.00 |      1964 |    9169000 |           37.2 |    3377.00 |    3922.00 | Zambia                                       | Republic                                     | Frederick Chiluba                    |    3162 | ZM    |
| 2441 | Tripoli | LBY         | Tripoli   |    1682000 | ZWE  | Zimbabwe                                     | Africa        | Eastern Africa            |   390757.00 |      1980 |   11669000 |           37.8 |    5951.00 |    8670.00 | Zimbabwe                                     | Republic                                     | Robert G. Mugabe                     |    4068 | ZW    |
+------+---------+-------------+-----------+------------+------+----------------------------------------------+---------------+---------------------------+-------------+-----------+------------+----------------+------------+------------+----------------------------------------------+----------------------------------------------+--------------------------------------+---------+-------+
478 rows in set (0.01 sec)

またも画面が文字で埋まってしまった。トリポリという都市は2つしかなかったはずなのに、なぜ478件も出てきてしまったのだろうか。それは、結合の対象にするカラム名を指定していないからだ。結合対象のカラムを指定していない場合、データベースの管理人は以下のように考えてしまう。

  1. city テーブルから、Name が Tripoli になっている行を取ってくる。2件だった。
  2. country テーブルから、全件取ってくる。239件だった。
  3. 結合対象カラムが指定されていないので、cityの結果1件ずつに、それぞれ239件のcountryの結果を組み合わせよう。2件 x 239件 = 478件の結果ができた。

これは望んだ結果ではないので、コマンドを修正しよう。city.CountryCodecountry.Code を結合してほしいので、指定を追加する。 INNER JOIN テーブル名 ON 結合対象指定 と入力するので、 SELECT * FROM city INNER JOIN country ON city.CountryCode = country.Code WHERE city.Name = 'Tripoli' となった。

mysql> SELECT * FROM city INNER JOIN country ON city.CountryCode = country.Code WHERE city.Name = 'Tripoli';
+------+---------+-------------+-----------+------------+------+------------------------+-----------+-----------------+-------------+-----------+------------+----------------+----------+----------+-----------+-------------------+--------------------+---------+-------+
| ID   | Name    | CountryCode | District  | Population | Code | Name                   | Continent | Region          | SurfaceArea | IndepYear | Population | LifeExpectancy | GNP      | GNPOld   | LocalName | GovernmentForm    | HeadOfState        | Capital | Code2 |
+------+---------+-------------+-----------+------------+------+------------------------+-----------+-----------------+-------------+-----------+------------+----------------+----------+----------+-----------+-------------------+--------------------+---------+-------+
| 2439 | Tripoli | LBN         | al-Shamal |     240000 | LBN  | Lebanon                | Asia      | Middle East     |    10400.00 |      1941 |    3282000 |           71.3 | 17121.00 | 15129.00 | Lubnan    | Republic          | Émile Lahoud       |    2438 | LB    |
| 2441 | Tripoli | LBY         | Tripoli   |    1682000 | LBY  | Libyan Arab Jamahiriya | Africa    | Northern Africa |  1759540.00 |      1951 |    5605000 |           75.5 | 44806.00 | 40562.00 | Libiya    | Socialistic State | Muammar al-Qadhafi |    2441 | LY    |
+------+---------+-------------+-----------+------------+------+------------------------+-----------+-----------------+-------------+-----------+------------+----------------+----------+----------+-----------+-------------------+--------------------+---------+-------+
2 rows in set (0.00 sec)

さきほどよりは平和な結果になった。しかし横長で見えづらいので、見たい情報だけに絞って表示しよう。 * の部分を見たいカラムの指定に変更する。だからといってただ単にカラム名を記述してしまうと、以下のようになってしまう。

mysql> SELECT ID, Name, CountryCode, District, Population, Name FROM city INNER JOIN country ON city.CountryCode = country.Code WHERE city.Name = 'Tripoli';
ERROR 1052 (23000): Column 'Name' in field list is ambiguous

また「曖昧だ」とエラーメッセージが出てくるので、同じようにテーブル名込みで指定しよう。

mysql> SELECT city.ID, city.Name, city.CountryCode, city.District, city.Population, country.Name FROM city INNER JOIN country ON city.CountryCode = country.Code WHERE city.Name = 'Tripoli';
+------+---------+-------------+-----------+------------+------------------------+
| ID   | Name    | CountryCode | District  | Population | Name                   |
+------+---------+-------------+-----------+------------+------------------------+
| 2439 | Tripoli | LBN         | al-Shamal |     240000 | Lebanon                |
| 2441 | Tripoli | LBY         | Tripoli   |    1682000 | Libyan Arab Jamahiriya |
+------+---------+-------------+-----------+------------+------------------------+
2 rows in set (0.00 sec)

しかし、結果の Name にテーブル名が含まれていないので、別名をつけてやることにする。

mysql> SELECT city.ID, city.Name AS CityName, city.CountryCode, city.District, city.Population, country.Name AS CountryName FROM city INNER JOIN country ON city.CountryCode = country.Code WHERE city.Name = 'Tripoli';
+------+----------+-------------+-----------+------------+------------------------+
| ID   | CityName | CountryCode | District  | Population | CountryName            |
+------+----------+-------------+-----------+------------+------------------------+
| 2439 | Tripoli  | LBN         | al-Shamal |     240000 | Lebanon                |
| 2441 | Tripoli  | LBY         | Tripoli   |    1682000 | Libyan Arab Jamahiriya |
+------+----------+-------------+-----------+------------+------------------------+
2 rows in set (0.01 sec)

また、別名と組み合わせることはできないが、 テーブル名.* を使ってカラム指定を省略することができる。* を使っていないテーブルのカラムには、別名をつけても問題ない。

mysql> SELECT city.*, country.Name AS CountryName FROM city INNER JOIN country ON city.CountryCode = country.Code WHERE city.Name = 'Tripoli';
+------+---------+-------------+-----------+------------+------------------------+
| ID   | Name    | CountryCode | District  | Population | CountryName            |
+------+---------+-------------+-----------+------------+------------------------+
| 2439 | Tripoli | LBN         | al-Shamal |     240000 | Lebanon                |
| 2441 | Tripoli | LBY         | Tripoli   |    1682000 | Libyan Arab Jamahiriya |
+------+---------+-------------+-----------+------------+------------------------+
2 rows in set (0.00 sec)
ふたつのテーブルに該当する行と、片方にだけある行を結合して取得する(LEFT OUTER JOIN, RIGHT OUTER JOIN)

ところで、 countryLanguage テーブルを見てみよう。

mysql> DESC countryLanguage;
+-------------+---------------+------+-----+---------+-------+
| Field       | Type          | Null | Key | Default | Extra |
+-------------+---------------+------+-----+---------+-------+
| CountryCode | char(3)       | NO   | PRI |         |       |
| Language    | char(30)      | NO   | PRI |         |       |
| IsOfficial  | enum('T','F') | NO   |     | F       |       |
| Percentage  | float(4,1)    | NO   |     | 0.0     |       |
+-------------+---------------+------+-----+---------+-------+
4 rows in set (0.01 sec)

CountryCode'JPN' になっているレコードを見てみると、以下のようになっている。

mysql> select * from countryLanguage WHERE CountryCode = 'JPN';
+-------------+----------------------+------------+------------+
| CountryCode | Language             | IsOfficial | Percentage |
+-------------+----------------------+------------+------------+
| JPN         | Ainu                 | F          |        0.0 |
| JPN         | Chinese              | F          |        0.2 |
| JPN         | English              | F          |        0.1 |
| JPN         | Japanese             | T          |       99.1 |
| JPN         | Korean               | F          |        0.5 |
| JPN         | Philippene Languages | F          |        0.1 |
+-------------+----------------------+------------+------------+
6 rows in set (0.01 sec)

日本で話されている言語と、話者の割合が管理されていることがわかる。 Language を指定すれば、その言語が話されている国と割合がわかる。

mysql> select * from countryLanguage WHERE Language = 'English';
+-------------+----------+------------+------------+
| CountryCode | Language | IsOfficial | Percentage |
+-------------+----------+------------+------------+
| ABW         | English  | F          |        9.5 |
| AIA         | English  | T          |        0.0 |
| ANT         | English  | F          |        7.8 |

(中略)

| WSM         | English  | T          |        0.6 |
| ZAF         | English  | T          |        8.5 |
| ZWE         | English  | T          |        2.2 |
+-------------+----------+------------+------------+
60 rows in set (0.01 sec)

英語が60カ国で話されていることがわかった。では、国の一覧と、その国の英語話者の割合を一覧にしてみよう。INNER JOIN で、 country テーブルの一覧と countryLanguage テーブルを Language = 'English' で絞り込んだ結果一覧とを結合する。

mysql> SELECT country.Code, country.Name AS CountryName, countryLanguage.Language, countryLanguage.IsOfficial, countryLanguage.Percentage FROM country INNER JOIN countryLanguage ON country.Code = countryLanguage.CountryCode WHERE Language = 'English';
+------+--------------------------------------+----------+------------+------------+
| Code | CountryName                          | Language | IsOfficial | Percentage |
+------+--------------------------------------+----------+------------+------------+
| ABW  | Aruba                                | English  | F          |        9.5 |
| AIA  | Anguilla                             | English  | T          |        0.0 |
| ANT  | Netherlands Antilles                 | English  | F          |        7.8 |

(中略)

| WSM  | Samoa                                | English  | T          |        0.6 |
| ZAF  | South Africa                         | English  | T          |        8.5 |
| ZWE  | Zimbabwe                             | English  | T          |        2.2 |
+------+--------------------------------------+----------+------------+------------+
60 rows in set (0.01 sec)

たしかに結果一覧が出たが、一覧には、英語を話していない国も一緒に含めたい。INNER JOIN では、結合するふたつのテーブル両方にある行しか結果一覧に含めない。そこで、この場合には OUTER JOIN を使用する。

mysql> SELECT country.Code, country.Name AS CountryName, countryLanguage.Language, countryLanguage.IsOfficial, countryLanguage.Percentage FROM country LEFT JOIN countryLanguage ON country.Code = countryLanguage.CountryCode AND countryLanguage.Language = 'English';
+------+----------------------------------------------+----------+------------+------------+
| Code | CountryName                                  | Language | IsOfficial | Percentage |
+------+----------------------------------------------+----------+------------+------------+
| ABW  | Aruba                                        | English  | F          |        9.5 |
| AFG  | Afghanistan                                  | NULL     | NULL       |       NULL |
| AGO  | Angola                                       | NULL     | NULL       |       NULL |

(中略)

| ZAF  | South Africa                                 | English  | T          |        8.5 |
| ZMB  | Zambia                                       | NULL     | NULL       |       NULL |
| ZWE  | Zimbabwe                                     | English  | T          |        2.2 |
+------+----------------------------------------------+----------+------------+------------+
239 rows in set (0.00 sec)

また、この場合には countryLanguage.Language = 'English' の条件を WHERE に含めず、LEFT JOIN 手^ブル名 ON の後に入力する。 WHERE に含めた場合は、以下のようになる。

mysql> SELECT country.Code, country.Name AS CountryName, countryLanguage.Language, countryLanguage.IsOfficial, countryLanguage.Percentage FROM country LEFT JOIN countryLanguage ON country.Code = countryLanguage.CountryCode WHERE countryLanguage.Language = 'English';
+------+--------------------------------------+----------+------------+------------+
| Code | CountryName                          | Language | IsOfficial | Percentage |
+------+--------------------------------------+----------+------------+------------+
| ABW  | Aruba                                | English  | F          |        9.5 |
| AIA  | Anguilla                             | English  | T          |        0.0 |
| ANT  | Netherlands Antilles                 | English  | F          |        7.8 |

(中略)

| WSM  | Samoa                                | English  | T          |        0.6 |
| ZAF  | South Africa                         | English  | T          |        8.5 |
| ZWE  | Zimbabwe                             | English  | T          |        2.2 |
+------+--------------------------------------+----------+------------+------------+
60 rows in set (0.00 sec)

結果が60件しか出てこず、 INNER JOIN と同じになっている。これは、上のSQLを管理人が以下のように理解するからである*7

  1. country テーブルから全件を取得する。結果は239件。
  2. countryLanguage テーブルから全件を取得する。結果は984件。
  3. 1と2の結果一覧を country.Code = countryLanguage.CountryCode の条件で結合する。
  4. 3の結果のうち、 countryLanguage.Language = 'English' に該当する行だけに絞り込む。結果は60件。

これを ON に書くことで、以下のように解釈してもらえます。

  1. country テーブルから全件を取得する。結果は239件。
  2. countryLanguage テーブルのうち countryLanguage.Language = 'English' に該当する行だけに絞り込む。結果は60件。
  3. 1に2を country.Code = countryLanguage.CountryCode の条件で結合する。1のデータのうち2に該当する行が無いものも、結合結果に含める。結果は239件。

A LEFT JOIN B の場合には A にしかないデータも結果に含まれ、 A RIGHT JOIN B の場合には B にしかないデータも結果に含まれる。LEFT と RIGHT とでは、このテーブルの結合の方向が異なる。

結合の説明はここまで。

テーブルにデータを追加する(INSERT INTO テーブル名 ( カラム名 ) VALUES ( データ ))

世界に新しい国ができたとか、country テーブルに不足しているデータがあった場合、データを追加する必要がでてくる。Excel では空白の行にデータを追加するが、MySQL ではコマンドでデータを追加する。追加先のテーブルを確認しよう。

mysql> DESC country;
+----------------+---------------------------------------------------------------------------------------+------+-----+---------+-------+
| Field          | Type                                                                                  | Null | Key | Default | Extra |
+----------------+---------------------------------------------------------------------------------------+------+-----+---------+-------+
| Code           | char(3)                                                                               | NO   | PRI |         |       |
| Name           | char(52)                                                                              | NO   |     |         |       |
| Continent      | enum('Asia','Europe','North America','Africa','Oceania','Antarctica','South America') | NO   |     | Asia    |       |
| Region         | char(26)                                                                              | NO   |     |         |       |
| SurfaceArea    | float(10,2)                                                                           | NO   |     | 0.00    |       |
| IndepYear      | smallint(6)                                                                           | YES  |     | NULL    |       |
| Population     | int(11)                                                                               | NO   |     | 0       |       |
| LifeExpectancy | float(3,1)                                                                            | YES  |     | NULL    |       |
| GNP            | float(10,2)                                                                           | YES  |     | NULL    |       |
| GNPOld         | float(10,2)                                                                           | YES  |     | NULL    |       |
| LocalName      | char(45)                                                                              | NO   |     |         |       |
| GovernmentForm | char(45)                                                                              | NO   |     |         |       |
| HeadOfState    | char(60)                                                                              | YES  |     | NULL    |       |
| Capital        | int(11)                                                                               | YES  |     | NULL    |       |
| Code2          | char(2)                                                                               | NO   |     |         |       |
+----------------+---------------------------------------------------------------------------------------+------+-----+---------+-------+
15 rows in set (0.00 sec)

追加するデータを用意して、INSERT コマンドを入力する。ここでは、オーランド諸島自治領が突然独立したものとして、データを追加する*8

mysql> INSERT INTO country ( Code, Name, Continent, Region, SurfaceArea, IndepYear, Population, LifeExpectancy, GNP, GNPOld, LocalName, GovernmentForm, HeadOfState, Capital, Code2 ) VALUES ( 'ALA', 'Aland Islands', 'Europe', 'Nordic Countries', 13517.00, 1920, 28700, 82.5, 0, 0, 'Landskapet Åland', 'Parliamentary System', 'Sauli Väinämö Niinistö', null, 'AX' );
Query OK, 1 row affected (0.02 sec)

追加した結果を SELECT して確認する。

mysql> SELECT * FROM country WHERE Code = 'ALA'\G
*************************** 1. row ***************************
          Code: ALA
          Name: Aland Islands
     Continent: Europe
        Region: Nordic Countries
   SurfaceArea: 13517.00
     IndepYear: 1920
    Population: 28700
LifeExpectancy: 82.5
           GNP: 0.00
        GNPOld: 0.00
     LocalName: Landskapet Åland
GovernmentForm: Parliamentary System
   HeadOfState: Sauli Väinämö Niinistö
       Capital: NULL
         Code2: AX
1 row in set (0.00 sec)

このままでは首都のマリエハムンが登録されていないので、Capital のデータを更新して、首都を設定しよう。DESC country; の結果を見ればわかるように、 Capital カラムは int(11) というカラムタイプになっている。これは「11桁の数字」という意味で、スウェーデンの場合、以下のようになっている。

mysql> SELECT Code, Name, Capital FROM country WHERE Name = 'Sweden';
+------+--------+---------+
| Code | Name   | Capital |
+------+--------+---------+
| SWE  | Sweden |    3048 |
+------+--------+---------+
1 row in set (0.00 sec)

この 3048 は、以下のように city テーブルの ID カラムの番号を意味している。

mysql> select * from city where ID=3048;
+------+-----------+-------------+----------+------------+
| ID   | Name      | CountryCode | District | Population |
+------+-----------+-------------+----------+------------+
| 3048 | Stockholm | SWE         | Lisboa   |     750348 |
+------+-----------+-------------+----------+------------+
1 row in set (0.00 sec)

オーランド諸島の首都マリエハムンを city テーブルで検索すると、以下のようになっている。

mysql> select * from city where Name='Mariehamn';
Empty set (0.01 sec)

無い。city テーブルにも追加する必要がある。

mysql> desc city;
+-------------+----------+------+-----+---------+----------------+
| Field       | Type     | Null | Key | Default | Extra          |
+-------------+----------+------+-----+---------+----------------+
| ID          | int(11)  | NO   | PRI | NULL    | auto_increment |
| Name        | char(35) | NO   |     |         |                |
| CountryCode | char(3)  | NO   | MUL |         |                |
| District    | char(20) | NO   |     |         |                |
| Population  | int(11)  | NO   |     | 0       |                |
+-------------+----------+------+-----+---------+----------------+
5 rows in set (0.00 sec)

確認すると、 city.ID には auto_increment という記載がある。これは「自動的に最大値+1を設定する」という意味で、新規にデータを追加した場合、何も指定しなければ最大値+1を設定してくれるようになっている。INSERT では基本的に ID は指定せず、以下のようにする。

mysql> INSERT INTO city ( Name, CountryCode, District, Population ) VALUES ( 'Mariehamn', 'ALA', 'Aland', 11186 );
Query OK, 1 row affected (0.03 sec)

追加結果を確認する。

mysql> select * from city where Name='Mariehamn';
+------+-----------+-------------+----------+------------+
| ID   | Name      | CountryCode | District | Population |
+------+-----------+-------------+----------+------------+
| 4080 | Mariehamn | ALA         | Aland    |      11186 |
+------+-----------+-------------+----------+------------+
1 row in set (0.00 sec)

ID4080 が割り当てられている。 これで、country テーブルの Capital を更新する準備ができた。

テーブルのデータを更新する(UPDATE テーブル名 SET カラム名 = 変更内容 WHERE 条件)

INSERT で追加したオーランド諸島の首都IDを、新規に追加した city.ID である 4080 で更新する。更新前、データは以下のように NULL (空データ)になっている。

mysql> SELECT * FROM country WHERE Code = 'ALA'\G
*************************** 1. row ***************************
          Code: ALA
          Name: Aland Islands
     Continent: Europe
        Region: Nordic Countries
   SurfaceArea: 13517.00
     IndepYear: 1920
    Population: 28700
LifeExpectancy: 82.5
           GNP: 0.00
        GNPOld: 0.00
     LocalName: Landskapet Åland
GovernmentForm: Parliamentary System
   HeadOfState: Sauli Väinämö Niinistö
       Capital: NULL
         Code2: AX
1 row in set (0.00 sec)

これを更新する。

mysql> UPDATE country SET Capital = 4080 WHERE Code = 'ALA';
Query OK, 1 row affected (0.01 sec)
Rows matched: 1  Changed: 1  Warnings: 0

更新結果を確認する。

mysql> SELECT Code, Name, Capital FROM country WHERE Code = 'ALA';
+------+---------------+---------+
| Code | Name          | Capital |
+------+---------------+---------+
| ALA  | Aland Islands |    4080 |
+------+---------------+---------+
1 row in set (0.00 sec)

UPDATE 時に WHERE で条件を指定しなかった場合、 country テーブルの全データが更新されてしまうので、意図していなければ気をつけてコマンドを入力しよう。実行した場合、以下のようになる。

mysql> UPDATE country SET Capital = 4080;
Query OK, 240 row affected (0.01 sec)
Rows matched: 240  Changed: 240  Warnings: 0

影響を受けた行が意図せず240行になっている。

データを削除する(DELETE FROM テーブル名 WHERE 条件)

先程追加したオーランド諸島のデータを削除してみよう。実施する前に、対象のデータを確認する。

mysql> SELECT * FROM country WHERE Code = 'ALA'\G
*************************** 1. row ***************************
          Code: ALA
          Name: Aland Islands
     Continent: Europe
        Region: Nordic Countries
   SurfaceArea: 13517.00
     IndepYear: 1920
    Population: 28700
LifeExpectancy: 82.5
           GNP: 0.00
        GNPOld: 0.00
     LocalName: Landskapet Åland
GovernmentForm: Parliamentary System
   HeadOfState: Sauli Väinämö Niinistö
       Capital: 4080
         Code2: AX
1 row in set (0.00 sec)

削除を実施する。しかし、以下のようにエラーになった。

mysql> DELETE FROM country WHERE Code = 'ALA';
ERROR 1451 (23000): Cannot delete or update a parent row: a foreign key constraint fails (`world`.`city`, CONSTRAINT `city_ibfk_1` FOREIGN KEY (`CountryCode`) REFERENCES `country` (`Code`))

これは、「country テーブルの削除しようとしているデータに紐付いている他のテーブルのデータがあるので、削除できない」というエラーだ。その他のテーブルは city で、カラム名CountryCode とのことだそうだ。つまり、 city テーブルにある CountryCode = 'ALA' のデータがあるので、country テーブルの Code = 'ALA' のデータも削除できないのだ。先に city テーブルのデータを削除する必要がある。削除する前に確認しよう。

mysql> SELECT * FROM city WHERE CountryCode = 'ALA';
+------+-----------+-------------+----------+------------+
| ID   | Name      | CountryCode | District | Population |
+------+-----------+-------------+----------+------------+
| 4080 | Mariehamn | ALA         | Aland    |      11186 |
+------+-----------+-------------+----------+------------+
1 row in set (0.00 sec)

確認してから、削除する。

mysql> DELETE FROM city WHERE CountryCode = 'ALA';
Query OK, 1 row affected (0.02 sec)

削除してから、また確認しよう。

mysql> SELECT * FROM city WHERE CountryCode = 'ALA';
Empty set (0.00 sec)

削除に成功したので、country からもデータを削除する。

mysql> DELETE FROM country WHERE Code = 'ALA';
Query OK, 1 row affected (0.02 sec)

削除してから、必ず確認する。

mysql> SELECT * FROM country WHERE Code = 'ALA'\G
Empty set (0.00 sec)

今度は削除に成功した。

ここまでで、基本的な確認とデータ操作の説明を終わる。ここからは、データベースやテーブルの定義、変更などを簡単に説明する。

データベースの定義(CREATE DATABASE データベース名)

性質の異なる情報を同じ場所に保存しておくと、整理や取り出す時に混乱のもとになりがちだ。あれもこれも入れられる箱は入れる時には何も考えなくて良いので便利だが、いざ中身を見るときには中は大変なことになっているだろう。それを防ぐため、必要に応じてデータベースを分ける。

mysql> CREATE DATABASE `world2`;
Query OK, 1 row affected (0.03 sec)

データベース定義文の確認(SHOW CREATE DATABASE データベース名)

一度作成したデータベースは、その作成時に使われたコマンドを再確認することができる。今作成した world2 データベースの定義文を見てみよう。

mysql> SHOW CREATE DATABASE world2;
+----------+-----------------------------------------------------------------+
| Database | Create Database                                                 |
+----------+-----------------------------------------------------------------+
| world2   | CREATE DATABASE `world2` /*!40100 DEFAULT CHARACTER SET utf8 */ |
+----------+-----------------------------------------------------------------+
1 row in set (0.00 sec)

/*!40100 DEFAULT CHARACTER SET utf8 */ の部分はデータベースで使用するデフォルトの文字コードの設定だが、文字コードについて説明し始めると長くなるのでここでは割愛する。

データベースの削除(DROP DATABASE データベース名)

定義済みのデータベースを削除することもできる。

mysql> DROP DATABASE world2;
Query OK, 0 rows affected (0.09 sec)

あっけなく消えてしまう。バックアップを保存していない場合などは気軽に復元できないので気をつけること。

テーブルの定義(CREATE TABLE テーブル名...)

データベース同様に、テーブルも定義することができる。

mysql> CREATE TABLE test ( id serial, name text, created datetime default now() );
Query OK, 0 rows affected (0.05 sec)

mysql> DESC test;
+---------+---------------------+------+-----+-------------------+----------------+
| Field   | Type                | Null | Key | Default           | Extra          |
+---------+---------------------+------+-----+-------------------+----------------+
| id      | bigint(20) unsigned | NO   | PRI | NULL              | auto_increment |
| name    | text                | YES  |     | NULL              |                |
| created | datetime            | YES  |     | CURRENT_TIMESTAMP |                |
+---------+---------------------+------+-----+-------------------+----------------+
3 rows in set (0.00 sec)

ここでは詳細は説明しない。カラムの種別などこまごまとした設定ができる。

テーブルの定義文を確認する(SHOW CREATE TABLE テーブル名)

こちらもデータベース同様に、一度定義されたテーブルは、その作成時に使われたコマンドを再確認することができる。

mysql> SHOW CREATE TABLE test;
+-------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table                                                                                                                                                                                           |
+-------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| test  | CREATE TABLE `test` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `name` text,
  `created` datetime DEFAULT CURRENT_TIMESTAMP,
  UNIQUE KEY `id` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 |
+-------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

テーブルの削除(DROP TABLE テーブル名)

定義済みのテーブルを削除することもできる。

mysql> DROP TABLE test;
Query OK, 0 rows affected (0.01 sec)

こちらもあっけなく消えてしまう。バックアップを保存していない場合などはやはり気軽に復元できないので気をつけること。

テーブルにカラムを追加する(ALTER TABLE テーブル名 ADD COLUMN カラム名 カラム設定)

カラムを追加して、定義済みのテーブルの内容を変更することができる。

mysql> ALTER TABLE test ADD COLUMN modified datetime default null ON UPDATE CURRENT_TIMESTAMP AFTER created;
Query OK, 0 rows affected (1.21 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> desc test;
+----------+---------------------+------+-----+-------------------+-----------------------------+
| Field    | Type                | Null | Key | Default           | Extra                       |
+----------+---------------------+------+-----+-------------------+-----------------------------+
| id       | bigint(20) unsigned | NO   | PRI | NULL              | auto_increment              |
| name     | text                | YES  |     | NULL              |                             |
| created  | datetime            | YES  |     | CURRENT_TIMESTAMP |                             |
| modified | datetime            | YES  |     | NULL              | on update CURRENT_TIMESTAMP |
+----------+---------------------+------+-----+-------------------+-----------------------------+
4 rows in set (0.00 sec)

テーブルからカラムを削除する(ALTER TABLE テーブル名 DROP COLUMN カラム名

定義済みのテーブルからカラムを削除することができる。

mysql> ALTER TABLE test DROP COLUMN modified;
Query OK, 0 rows affected (0.07 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> desc test;
+---------+---------------------+------+-----+-------------------+----------------+
| Field   | Type                | Null | Key | Default           | Extra          |
+---------+---------------------+------+-----+-------------------+----------------+
| id      | bigint(20) unsigned | NO   | PRI | NULL              | auto_increment |
| name    | text                | YES  |     | NULL              |                |
| created | datetime            | YES  |     | CURRENT_TIMESTAMP |                |
+---------+---------------------+------+-----+-------------------+----------------+
3 rows in set (0.00 sec)

コマンドを入力していて起こりがちなトラブル

最後に簡単に、よく起こりがちなトラブルについて触れておく。

コーテーションのとじ忘れ

以下のような表示になり、コマンドが終えられなくなってしまうことがある。

mysql> SELECT * FROM country WHERE Name ='Japan;
    '> 

エンターキーを押し続けても、以下のように下に流れていくだけだ。

mysql> SELECT * FROM country WHERE Name ='Japan;
    '>
    '>
    '>

これは 'Japan のように ' が不足しているから起きている現象で、次の ' を待っているので、コマンドが終わらなくなってしまっている。'; を入力してエンターキーを押すと、以下のように待ち状態を抜けられるので、改めて入力しよう。

mysql> SELECT * FROM country WHERE Name ='Japan;
    '>
    '>
    '> ';
Empty set (0.01 sec)

他にあるあるなトラブルを思い出したら追加していく。

終わりに

なんだか随分長くなってしまった。書いてみて思ったが、やはりテーブルの結合が初学者のつまづきがちなポイントだと思う。とはいえ何度か使って慣れていけば、何も問題はないと思う。また、ここで説明した内容はデータベースサーバの持つ機能のうちのほんの一部でしかないし、説明を簡単にするために敢えて触れていないことや簡略化したことも多くある。データベースサーバには、大抵のやりたいことは叶えてくれるだけの機能が用意されているし、そのために内部はもっと複雑になっている。ぜひ、実際の動作やマニュアルをあたってみてほしい。

などと偉そうに書いてみたものの、調べてみると自分自身知らないことがたくさんあったので、それらについてここで説明しないまでも、とても勉強になったのでした。知識を自分なりにまとめるってすごく大切。

*1:なので、以下の説明は関係データベース管理システムだけを含み、列指向データベース管理システムや分散クエリエンジンなどは含みません

*2:関係データベース管理システムRDBMS

*3:異論はあると思いますが自分はこう

*4:彼女に?

*5:理解しやすくするために意訳してあります

*6:もちろんわかる人にはわかるが

*7:ただし EXPLAIN を見るとわかるように、実際の動作は異なります

*8:ただ、割とデータは適当です

イタリア語版ミュータント・アンド・マスターマインドの舞台裏 〜エマヌエーレ・グラナテッロ、ミルコ・ペリチオーニ・インタビュー〜

執筆者:ルイージ "テンカー" カラファ (Luigi "Tencar" Carafa)
2018年2月1日 本家イタリア語版公開
2018年2月5日 非公式日本語版公開


まったくの偶然の出来事から記事が生まれることがあります。D&D(ダンジョンズ&ドラゴンズ)の優れたマスターであるエンリコ・カンティーレ(Enrico Cantile)とのチャットの中で、スーパーヒーローたちが活躍する宇宙を舞台にしたRPGミュータント・アンド・マスターマインド(Mutants & Masterminds)に出会ったこともまたそういった偶然の出来事です。

私はそれからイタリア語版RPGを制作担当したチームを探しはじめ、カイゾク・プレス社(Kaizoku Press)のエマヌエーレ・グラナテッロ(Emanuele Granatello)ミルコ・ペリチオーニ(Mirko Pellicioni)へのインタビューに成功しました。


ルイージ(筆者): ミュータント・アンド・マスターマインドをイタリアに持ち込むというアイデアは、いつどこで思い浮かんだのですか?

エマヌエーレ: 僕とミルコ・ペリチオーニとは何年も前からの知り合いで、僕たち(と、レリオ・ムーラス(Lelio Mulas)のことも忘れちゃいけないね)は、これまでに様々なプロジェクトに携わってきました(Fading Suns、Warcraft、ほかに実現はしませんでしたが Casal Pusterlengo 市郵便局の強盗)。僕は何年も前から、ウォーハンマーRPG第2版のテスト協力を通じてその編集者*1のことを知っていました。
僕たちは何か新しいものをもたらし、イタリアのRPG業界で勝鬨をあげたいと思っていました。それに僕たちはDCコミックスの『Legione』*2以来(ああ、Legione!懐かしい!)、本当に "インパクトのある" ものは見たことがなかった。そしてミュータント・アンド・マスターマインドは、これまた偶然にも、DCコミックスのゲームです(アメリカでは『DC アドベンチャーズ』という名前で出版されています)。

贔屓していると思われるかもしれませんが、それでも本当に、ミュータント・アンド・マスターマインドスーパーヒーローのPRGだと思います。システムはすぐ覚えてすぐ遊べるようになっていて、信じられないぐらい柔軟です。おおむねD&Dと共通していますが、異なる点が2つあります。例えばより早いダメージの回復バットマンシリーズの世界観でプレイしたい場合におあつらえ向き)、ポイントシステム(ゲームの柔軟性を、最近の他のゲームのレベルまで高めています)、複合システムはヒーローポイントのメカニズムにリンクしています(こういったジャンルの典型的なヒーローの行動とダークヒーローのジレンマとをすっきり解決し、いわゆる "ショッピングカート" のようにしてしまうことはありません)。
ヒーローパワーのシステム(本の中でも最も重要なルールです)についても、いわゆる "呪文のリスト" のようになることを避けるため、非常に簡潔にまとめられています。それにくわえて、キャラクターやアーキタイプのラピッドジェネレータ(高速生成システム)が用意されているので、プレイヤーは基本ルールブック(オールカラーで豊富なイラストつき)の中の、何十種類ものキャラクターやパワーをすぐに使用することができるようになっています。それから、主な設定を紹介するための二つのストーリー!ほかになにか必要ですか?もし足りないものが見つかったとしたら、僕は追加のエキスパンションを使います。ウェブサイトに用意されている基本ルールを直接ダウンロードしてみてください!

また、ミュータント・アンド・マスターマインドのマスタースクリーンや、PGやヴィランをより迅速に作成するために用意された、能力に関する分厚い(700ページ以上!)ルールブック、『パワープロフィール』ゲームマスター向け、冒険やアドバイス、アイデア、いくつものサプライズが掲載されています。たとえば、魔法の章をご覧ください)も作りました。

ルイージ: 全体の翻訳をするのはどれぐらい難しいことですか?あなたがたにこの作品を委ねたのは誰ですか?

ミルコ: エマヌエーレとレリオの担当した翻訳作業には、かなり時間がかかりました。翻訳作業は、自分の言葉で文章をまとめるというだけでなく、通して読み、再読し、印刷してからさらに読み直し、第三者に読んでもらうことまでが含まれます(僕たちが変えなきゃと思っている作業もあります...時間がかかりすぎるからね!)。これが翻訳者の仕事です(模倣もありますが、これこそが本当の翻訳なのです)。グリーン・ローニンのスティーブ・ケンソン(Steve Kenson)とジョン・リーセウッサー(Jon Leitheusser)にも、ルールへの疑問や誤植の解消に関して、多大な助力をいただきました。グラフィックやレイアウトも尋常ではありませんでした。ミルコ・ペリチオーニ氏は最終段階での変更という、印刷上の大問題に直面しなければならなかった。それは銀河に打ち上げられた宇宙の侮辱であり、宇宙人の科学者たちは、将来ビッグバンの背景放射と一緒にこれについて研究することでしょう。

ルイージ: ルールブックのフォーマットはどのようにして決めましたか?

ミルコ: 単なるA4サイズではなく、本家同様にアメリカの用紙形式にすることを話し合って決めました。カバーについては、顧客に安く提供することを考え、最終的なコスト増につながるハードカバーでの装丁ではなくペーパーバックを採用しました。

エマヌエーレ: 当初僕は編み物のカバーを考えていましたが、ミルコは僕に、あまり独創的でないものを使うよう納得させました。僕たちは古風なソフトカバー装丁を、レギュラー版、そしてとても稀少なルッカ*3版(現在では手に入らず、タイプミスが目立ちます。Gronchi Rosa*4のような感じです)のふたつで採用しました(純粋なスーパーヒーロースタイルで)。ウールカバー付きの版なら冬のゲームプレイにも役立つし、きっと綺麗だったのに、残念です。(暖かさで世界は救われます。でも…Mr.Freeze にとってはダメでしょうけどね!)



ルイージ: あなたがたもRPGのプレイヤーですか?

ミルコ: 僕は1987年以来RPGを精力的に遊んできました。そもそもがRPGの大ファンで、それなしに今まで生きるのがまず無理でした。

エマヌエーレ: 間違いないですね。僕は1993年に(僕は同僚ほどのオタクではありません)、D&Dの赤箱*5からでした(正直に言うと、僕より先にRPGで遊んでた人たちは、ヒーロークエストをやっていました)。僕の一番のお気に入りは、いまだにウォーハンマーRPG(初版と第二版)です。でも、他にもすごく雑食に遊んできました。
僕はスター・ウォーズRPGも好きだし、クトゥルフの呼び声、Rifts(ああ、Rifts…サイバー女とロータリー、喜びと悲しみ)、ミュータント・クロニクルズ、Fading Suns、パラノイア…などなど。もちろん、様々な派生版やたくさんのD20で、AD&D(アドバンスト・ダンジョンズ&ドラゴンズ)も遊んできました。ミュータント・アンド・マスターマインド(とTrue20、その後継)は、システムの到達点だと僕は思います。僕にはスティーブ・ケンソン(Steve Kenson)*6がどうやってそれに成功したのかはわかりませんが、70年代にもたらされた残滓*7は、柔軟かつシネマチックな珠玉作を産み出しました(ヒットポイントとレベルなしで!)。

ルイージ: RPGに対する情熱の源はなんですか?

ミルコ: すべては、1987年に僕が最初にローンウルフのゲームブック、それからThe Dark Eye*8と出会い、RPGがいったいどういうものなのかまったく知らないままに購入したときから始まりました。幸いなことに、僕の住んでいたボローニャには僕のような愛好家の集まるグループ(“Circolo degli Scacchi”や“Ludoteca giocano i Grandi di Corticella”)がたくさんありました。もちろん、僕にとっての二番目のRPGはD&Dで、それからルーンクエストクトゥルフの呼び声、そのほかにもたくさんあります。僕は古くからの愛好家のひとりですが、過去15年間の新しい革新的なゲームのことも大好きです。

エマヌエーレ: 子供の頃から、女の子に人気がなく、何事にもうんざりしていて、その名に相応しいコンピューターやコンソールも持ち合わせていない、基本的にオタクになろうとしていた僕(彼らにまだのど仏が無かったころ、永遠の天使たち!)には2つの選択肢しかありませんでした。シリアルキラーになるか、はたまたRPGか。僕はその両方の活動に専念していて、はっきりとどちらかを選んだわけではありませんでした。僕はカゼルタにある僕の大きな家で、長いRPGセッションを開催しましたが、それが終わる頃には、プレイヤーもキャラクターも消えてしまいました(必ずしもこの順序ではありません)。事態が悪化しはじめた時、僕は今住んでいる日本へと移りました。RPGを遊ぶことは少なくなりましたが、翻訳することは増えました。

ルイージ: 紙のマニュアルの出版を補助する目的で、タブレットやその他のサポートが可能なPDF形式やその他形式で配布するというアイデアについては考えましたか?

エマヌエーレ: はい。僕たちカイゾク・プレス社としてはそれについて考慮していて、2018年にはニュースをお知らせできます。僕は、僕たちが転換点にいると確信し続けています。紙はデジタルに道を譲りました。その道はまもなくウールに譲られることでしょう。ウールこそが未来です。その間に、僕たちはすでにデジタル配信を開始しており、僕たちのオンラインショップ(http://mutantsandmasterminds.it/ および http://kaizokupress.it/)では、紙でも購入することができます。Drivethrurpg*9 でも購入できます。さらに、Terra dei Giochi(僕たちと提携している数少ないショップのうちのひとつ)では、Webサイト terradeigiochi.it 上で、ミュータント・アンド・マスターマインドのために作られた数多くの素材(公式・非公式ともに)を取り扱っています。また、昨年12月にはイタリアで作られた最初のアドベンチャーが出版されました(無料)。なんとあのLegion (ああ、Legione!懐かしい!)の著者、ダニーロモレッティ(Danilo Moretti)によって書かれました。

ルイージ: これからの計画はありますか?すでに作業場に置かれている仕事はありますか?

ミルコ: たくさんのプロジェクト、アイデアだけでなく、他のチームとのコラボレーションや共同制作もあります。カイゾク・プレス社として、現時点では、ジョン・コヴァリック(John Kovalic)によって描かれた『子供たちを食べるコボルドたち』という、ビールとポテトチップから生まれた最初のRPGが作業場に置かれています!その後には、もっと古典的なほうへ移って、M&Mとメガ・アドベンチャーのための一組のエキスパンションと、すでにアメリカで販売されているD&D第5版のためのダンジョン・エポック『Rappan Athuk』の翻訳を行います。端的に言えば、僕たちの名前を信じてください(『Kaizoku』は日本語で『海賊』を意味します)。僕たちは新しい手法の出版で、みなさんを驚かせます(とにかく買って買って、買ってください!できれば、僕たちのサイトで)。

ルイージ: あなたがたのこれからの意欲に感謝を!

出典: https://nerdando.com/2018/02/01/ce-dietro-ledizione-italiana-mutants-masterminds/

*1:クリス・プラマス(Chris Pramas)、グリーン・ローニン(Green Ronin)の代表。RPG以外に食べ物、ウォーゲーム、パンク音楽、ドナルド・トランプとその支持者をからかうことの4つに情熱を燃やしている

*2:訳注: 1990年に発売されたアメコミ

*3:ルッカゲームズ。イタリア最大のアニメ・ゲーム・漫画イベント。年一回ルッカ市で開催される。

*4:イタリアの切手。描かれていたペルーとエクアドル間の国境に誤りがあり、訂正版が印刷された。

*5:ダンジョンズ&ドラゴンズ・スターターキットのこと

*6:ミュータント・アンド・マスターマインド、True20 などのゲームデザイナー。

*7:訳注: 1974年に誕生したダンジョンズ&ドラゴンズのこと。

*8:訳注: 1984年に出版されたRPG

*9:訳注: RPG関連のWeb通販サイト。http://www.drivethrurpg.com/

抽象的な無関心を欲する

何かをしなければ損をする。
何かをしなければ得をしない。
何かをすれば損をする。
何かをすれば得をしない。

そういう価値観を持つことを強要されることにすごく疲れる。

何かをしないことは、ただ何かをしなかったことであるべきだし、同様に、何かをしたことは、ただ何かをしたことであるべきだと思う。

他人からお金を取るための口実に、その人が何かをしなかったことや、何かをしたことを使わないでほしい。

宣伝の声が騒がしすぎる。