なぜですか

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

「ごめんなさい」は失礼か

「ごめんなさい」は目上の相手に使うべきではないという話をしばしば耳にする。

ある界隈では、「ごめんなさい」は目下に、「すみません」は目上に使うべきだとされているらしい。曰く「『ごめんなさい』の『ごめん』は許諾を、『なさい』は命令を意味するため、「許しなさい」という言葉に由来するからだ」ということらしい。

また別の界隈では、「ごめんなさい」はその場で謝って取り返しがつくようなことに対して使い、「すみません」はもうどうしようもないことに使うべきだとされているらしい。曰く「『すみません』は『もう謝っても済むことではありません』だから」ということらしい。

そんなこと考えて使ってる?

いずれも、文法や由来を理由に「使ってはいけない」「正しくはこう」というルールを説明しようとしている。しかし、普段私たちは、言葉を使う際に文法や由来など気にかけているだろうか。たとえば、「こんにちは」は「今日(こんにち)はご機嫌いかがでしょうか」の省略が由来*1なので、言われた相手は機嫌について答えなければならないなどと、考えたことがあるだろうか。自分はない。

また、「幸」の漢字はその由来が「手かせをされることを免れた人の姿」とも「男女の性交の様子」とも言われていて諸説あるが、はたしてそれと意識して使っているだろうか。幸子さんはそう意図して名付けられているのだろうか。決してそんなことはないはずだ。

「絆(きずな)」という言葉の由来(動物を繋いでおく綱)を持ち出してきて、「絆という言葉を使う人々のことが気に入らないと思っていたが、その理由がわかった」などという言説が Twitter において流れていたが、それについても、「絆」という言葉を使うその人々が「動物をつなぐ綱」だと意識して使っていない限り、そういった人々について嫌な感情を持つことと言葉の由来とはまったくの無関係であって、その「理由」は的外れだと言わざるを得ない。

大切なのは由来や文法ではない

つまり何が言いたいかというと、「使ってはいけない」「正しくはこう」と言いたい人がそう言ってしまうのは、文法的におかしいからとか、由来がどうこうだからではなく、単にその人にとって気に入らないからだということだ。その理由は様々だと思うが、言葉が耳慣れないように感じるということや、今までの習慣と外れるのが嫌だということ、その他に、言葉はともかくそもそも相手自体が気に入らないということもあるだろう。

いざ気に入らない他人の行動をやめさせたいが、ただ「嫌だからやめろ」と言うのでは、不服を唱えられたり、いかにも自分の権力を振りかざしているようで具合が悪かったりするのだろう。そこで初めて文法や由来が持ち出されてくる。文法や由来に照らし合わせることで、それが大勢のように思われてきて、相手の納得を得られやすいうえに、自分の直接さが目立たなくなる。姑息な(その場しのぎの)手段が利便性ゆえに常套化したのではないだろうか。

自分の目的を見誤るな

しかし言葉は決して、相手の行動を束縛したり、相手との関係性において優位に立ったり(マウントを取ったり)、攻撃したりするためだけに存在するのではない。言葉は道具であって、それが使われる際には使う人の伝えたい意図があるはずだ。

「ごめんなさい」は、単純な謝意であることが大半だ。もちろん言い方によってはそうではないこともある。しかし、それに由来や文法は関係ない。謝意以外のものを感じ取った際に言うべきは「ごめんなさいを使うな」ではなく、「不満があるなら言ってみて」や「心から謝ってほしい」であるはずだ。もしも相手の言葉に心からの謝意を感じたのであれば、素直にその気持ちを受け止めるべきだ。「ごめんなさいは、由来からしてふさわしくない」などと言うべきではない。

「絆」を使う人々に対して言うべきは「そんなに浅い関係性を、自分は絆と呼びたくない」ということであって、「絆という漢字は〜」といった、言葉狩りに繋がりかねないような持って回った講釈ではない。場合によっては素直に「お前らが気に入らない」と言うべきだ。

じゃあ「サーセン」でいいのかよ

だからといって、もし「気持ちがこもっていればいいなら、どんな言い方でもいいですよね」と感じたなら、それは自分本位が過ぎるように思う。たとえば「『ごめんなさい』の代わりに『サーセン』と言ったっていいじゃん」という気持ちは、はたして「ごめんなさい」で伝わるのと同等かそれ以上の謝意なのだろうか。意図が伝わるのなら、深く頭を下げて「サーセンした!!!!!」と言ってもいいとは思う。しかし「じゃあそれでいいじゃん」としてしまうのは、誠意の欠如が相手に伝わってしまうのではないだろうか。

言葉は一方が発信しただけでは意味がなく、受け取られて、理解されて初めて意味があるものだと思う。受信側の素直な受け取りや理解はもちろん必要だが、発信側にも、より意図が伝わりやすい言葉の選択が必要だ。「サーセン」と「ごめんなさい」とのどちらがより自分の意図が伝わりやすいか、考えるべきだと思う。

それをもって、もっと素直な世の中になればいいのに、などと思う、最近。

*1:もちろんこれにも諸説あります

鎌でガラスを掻く

2年ぶりに海で泳いだ。場所は南紀白浜白良浜(しららはま)。『白い砂浜に青い空』という情景描写がお世辞抜きにそのまま当てはまる海だった。朝から出かけて、体が熱くなったら泳ぎ、疲れたら休み、日が落ちる少し前に旅館へ戻った。そしてその結果、脚が猛烈に焼けたのだった。

あとから聞いた話では、白良浜のように砂が白く海の透明度が高い場所では、海面での屈折や砂からの反射によって、海に浮かんでいるときのほうが日に直接照らされているよりも、日焼けが進行するらしかった。しかしそれはあくまであとから聞いた話なのでもう遅く、その日の夜、温泉につかろうとしたところを、両脚への激痛と後悔とが襲ったのだった。

平日だったこともあり、温泉内には他に誰もいない。「あ〜〜〜〜〜〜いて〜〜〜〜〜〜」などと呻きつつ、我慢して肩まで浸かった。そのまましばらく湯の中にいると、両脚の痛みに慣れはじめ、お湯からの痛みに耐えるという感じではなくなってきた。

そうしてやっと、あらためて周囲の状況を確認することができた。

ガラスのむこうにカマキリがいた。

4センチ程度の、まだ小さく若いカマキリだった。木製の窓枠の上に立ち、両腕のカマを使って、ガラス窓をひっかいていた。そうしているうちに、たまにとっかかりが見つかり、そのカマキリはガラス窓を登りはじめた。と思うと、次の瞬間窓枠に落ちる。なかなか諦めることなく、それを繰り返した。

カマキリにとって、このガラス窓は何だろう。自然界には存在しない人工物で、きっとこれまでの進化の過程の中でそれが存在した期間があまりに短く、種として適応することが未だできていない構造物。そのために、登りたくとも上手く登攀することができない物。そこに何かがあることはわかっても、全体像を掴むことすらままならない何か。

ヒトにとって、そういうものはあるだろうか。ヒトの腕ではうまく登れず、理解することもままならない何か。仮にそれがあったとして、幾人かはその存在に気付くことはできたとしても、人類全体としてその感覚や知識を共有できるだろうか。ここでは具体的に明らかにはしないが、最近評論を読んだ物事が、そのガラス窓と重なって見えた気がした。

カマキリの上方にはガがいた。

有翼のガは、このガラス窓をどう理解しているのだろう。カマキリとは違い、ガラス窓の上のほうに留まることができる。同様に、下方に留まることもできる。しかして、全体像は把握できるだろうか。その点について同じ性質を持つ木や岩と、どう違うのだろう。もしかしたら彼らの世界観では、いずれも「留まるもの」と呼ばれているのかもしれない。

もっと脚力のある虫、たとえばカナブンはどうだろう。ガラス面は無理でも、木枠の横辺を伝って上に登ることはできる。ぐるっと一周して空間を認識できるとしたら、彼らにとってのガラス窓は、「四角い道」なのかもしれない。もしかしたら飛んでぶつかることで、「面」として把握できるかもしれない。

ガラスの反対側にはヒトがいる。

ちょっとやそっと温泉に浸かったところで脚の痛みは引くわけもなく、またいくらか呻きながら湯をあがり、その場を後にした。

書かずにはいられない生き物

北原白秋の『フレップトリップ』や『五足の靴』、和辻哲郎の『イタリア古寺巡礼』を見るにつけ思うにつけ、世の中には書かずにはいられない生き物たちが存在することを改めて実感する。何かを経験すればそれを書き留め、何かを思えばそれもまた書き留め、そのうちのいくつかは世に出され衆目を浴び、またいくつかは蔵されているのだということがなんとなく想像できる。

北原白秋 フレップ・トリップ

五足の靴 (岩波文庫)

五足の靴 (岩波文庫)

イタリア古寺巡礼 (岩波文庫)

イタリア古寺巡礼 (岩波文庫)

城北古書展

神田神保町で、5/4〜5の日程で城北古書展が開催されていた。どうやら毎年恒例のようで、しかし自分の場合は今回が初の立ち寄りだった。何がきっかけで知ったのだったか、おそらくは Twitter だったと思う。

会場は、神保町から御茶ノ水に向かう方面に建っている東京古書会館というビルで、その地下1階だった。入り口には店主らしき人たちが並ぶレジがあり、その脇を通った奥に、仮設の棚が並んでいる。書籍は2010年台の新しいものから江戸時代のものまで幅広くあり、またそれ以外に、古いパンフレットや掛け軸、切手などなどがわらわらと置かれていた。そんな棚の間を中年〜老齢の男性・女性たちが腕組みしながら歩き回り、あるいは手にとって見、あるいは棚を舐めるように眺めるなどしていた。

古書の価格は概ねどれも安くなっていて、普段1000円で出ている本が500円、200円のものが100円などと、だいたい通常の半額になっていた。理由はわからないが、持ち帰るのが面倒なのでここで売ってしまいたいのかとも思われた。うぶ荷*1もあったそうだが、それとわかるような目印があるわけではなく、素人の自分には全く見分けがつかなかった。

そこでは、普段200円がついている新書を100円で2冊買い、大型本を1冊購入した。和漢書もかなりお手頃価格で出ていたので、記念に購入しようかとも思ったが、漢文が平易に読めないことと、興味のわく内容のものが見つけられなかったので、今回は手出ししないことにした。城北展で書籍を購入すると、普通のスーパーの袋に本を入れてくれる。これもまたなんとなく非日常的で気に入った。

神保町古書巡り

古書会館を出ると、スーパーの袋を提げたまま神保町の古書店街に戻った。自分の場合はいつもだいたい同じルートで巡っていく。神保町の表通りにある澤口書店の巌松堂ビル店に始まり、西へ交差点を目指して向かいながら店先や店内を眺め、信号を渡ったら古書センタービルを登り、降りたらひとつ南の通りからまた東に戻っていき、やがて三省堂書店へたどる。SFを探すときにはアットワンダーや羊頭書房を覗くこともあるが、この日の目的はそれではなかったので、店先の廉価本の棚を眺めるにとどめた。

これをだいたい3周ぐらいすると、なんとなく疲れてくるので、喫茶店を探すことになる。この日も神保町で有名なさぼうるへダメ元で向かってみたが、「GW中は休業」との張り紙に出迎えられ、ほかに店を探す気力も残っていなかったので、そのまま神保町での休憩を諦めて帰路についた。

そんなに本を読むほうではない

と言うと奥さんからは全力で否定されるのだが、世の中には自分以上にもっと読む人がいることがわかっているので、本当に自分は読むほうではないと思っている。書棚にはたくさん本が溢れているし、身の回りでは読むほうだとも思ってはいるのだけれど、自分は読むのも遅いし、何時間も集中して読める本が減ってきたしで、「読むほうです」とはおこがましくて名乗りづらいと感じる。

冒頭の「書かずにはいられない生き物」と対応して、「読まずにはいられない生き物」と自称することも考えられなくはないが、どうか。

*1:まだお客の目に触れていない商品

「食べ始まる」考

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

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

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

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

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

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

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

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

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

終わる

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

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

終える

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

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

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

始まる

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

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

始める

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

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

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

言い換えて考えてみる

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

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

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

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

いざ使ってみる

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

「食べ始まる」の場合

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

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

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

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

「歌い始まる」の場合

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

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

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

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

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

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

他の表現で十分だから

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

開始の感覚の問題

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

開始の責任の所在の問題

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

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

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

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

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

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

連続を失敗

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

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

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

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

履いてみる。でもやっぱり変な感じがする。ニューバランスゴアテックスが使われているものだから、十中八九これだと思うし、社内にほかに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/