技術書典2で「SQL高速化」の同人誌が完売

新刊「SQL高速化 in PostgreSQL」表紙画像

おはよう諸君。今さらではあるが、当機関は技術書典2で「SQL高速化 in PostgreSQL」という同人誌を出して完売したので、その記録を留めておく。なおPDF版を販売中なので、興味のある人はそこのサンプルページを見てほしい。

同人誌の概要

同人誌のタイトルを改めて紹介すると「SQL高速化 in PostgreSQL ―ポスグレの力を解き放つSQLの書き方―」。内容は、ポスグレにおけるSQLチューニングのテクニック集。

ポスグレのSQLチューニング例はGoogle検索すればいくつか出てくるけど、「インデックスを張りましょう」とか「相関サブクエリを避けましょう」のような基本的な内容が多い。でもそんなの、もう知ってるよね?インデックスは張ってるし、相関サブクエリにもなっていない、そこから先はどうやってチューニングしたらいいの?…という質問に答える内容になっている。

特に最後の章は、同じ内容のクエリをいろんな書き方で書いてみて、ベンチマークを取っている。

SQLクエリ1クエリ2メモリ消費量
(A) 相関サブクエリ 1262.8ms514.6ms
(B) 導出テーブル 321.4ms164.6ms
(C) joinの順番を変更 229.1ms171.0ms
(D) in演算子+サブクエリ 228.4ms152.6ms
(E) in演算子+with句 248.4ms173.6ms
(F) distinct on 226.6ms221.2ms
(G) ウィンドウ関数 340.7ms321.4ms
(H) 非等値結合 381.8ms62.9ms

相関サブクエリ、おっそ!そしてクエリ2では非等値結合が速すぎ!同じ内容のSQLのはずなのに、書き方ひとつでここまで実行速度に差がつくのだから、SQLのチューニングは難しくもあり、面白くもある。

それから、SQLアンチパターン「ファントムファイル」の反論も書いてある。「SQLアンチパターン」はとても勉強になる本だけど、どう考えても「ファントムファイル」だけは間違いだと思うので、かわりにどうすればいいかを5.7章に書いてある。前から書きたかったので、この機会に書けたのはよかった。

なお対象読者は、SQLの勉強をした初級者〜中級者を想定している(まったくのSQL初心者は対象外)。初級者ではすべてを理解するのは難しいので、わかるところだけをつまみ食いすればよい。中級者は全部理解してほしいので、掲載しているSQLを自分で実行してみて、本当に高速化するのか確かめて欲しい。

目次

全部を表示する

  • 第1章:テーブル結合の削減
    • 1.1 サブクエリを使って結合を減らす
    • 1.2 集計してから結合する
    • 1.3 limitしてから結合する
    • 1.4 データを重複させることで結合を減らす
    • 1.5 アプリケーション側で結合する
  • 第2章:SQLチューニング
    • 2.1 削除フラグがついたデータを高速に除外する
    • 2.2 遅いクエリを複数の速いサブクエリで置き換える
    • 2.3 orderby~limit1よりmax()やmin()
    • 2.4 distinct onを使う
    • 2.5 クエリで利用できるメモリ量を増やす
    • 2.6 小さいテーブルどうしを先に結合する
    • 2.7 in演算子の右辺は重複データを減らす
    • 2.8 サブクエリを使って計算式の実行回数を減らす
    • 2.9 たくさんの行を挿入する
    • 2.10 複数のupdate文を1つにまとめる
    • 2.11 データがあれば更新し、なければ作成する
    • 2.12 returingを使って余分なselect文をなくす
  • 第3章:インデックス
    • 3.1 インデックスを付け忘れるケース
    • 3.2 インデックスが使われないケース
    • 3.3 インデックスの使用回数を調べる
    • 3.4 複合インデックス
    • 3.5 式インデックス
    • 3.6 部分インデックス
    • 3.7 巨大なテーブルにインデックスを追加する
    • 3.8 存在しない場合のみインデックスを作成する
    • コラム:使用するインデックスを指定する
  • 第4章:プログラムよりSQL
    • 4.1 木構造を再帰的にたどる
    • 4.2 2つの軸で集計する#1
    • 4.3 2つの軸で集計する#2
    • 4.4 2つの軸で集計する#3
    • 4.5 累計を計算する
    • 4.6 欠けている日付を埋める
    • 4.7 1:Nのデータを1:Nのまま取得する
    • 4.8 データをJSONで取得する
    • 4.9 CSVを生成する
  • 第5章:テーブル設計
    • 5.1 必要なカラムだけを取得する
    • 5.2 あまり使わないカラムを別テーブルに分離する
    • 5.3 サイズの大きいカラムを別テーブルに分離する
    • 5.4 更新の多いカラムを別テーブルに分離する
    • 5.5 サイズの小さいデータ型を選ぶ
    • 5.6 複数列属性を使ってテーブルサイズを減らす
    • 5.7 画像はファイルパスのみを保存する
    • 5.8 テーブルにfillfactorを設定する
  • 第6章:パーティショニング
    • 6.1 パーティショニングの概要
    • 6.2 パーティションの作成方法
    • 6.3 パーティショニング時の検索
    • 6.4 絞り込みがうまくいかないケース
    • 6.5 インデックスと外部参照キー
    • 6.6 パーティションの自動生成
    • 6.7 insertの高速化
    • 6.8 トリガー関数の高速化
    • 6.9 テーブルの削除
    • 6.10 補足事項
    • 6.11 参考文献
  • 第7章:SQLベンチマーク
    • 7.1 課題の説明
    • 7.2 テーブルとサンプルデータの作成
    • 7.3 (A)相関サブクエリ
    • 7.4 (B)導出テーブル
    • 7.5 (C)joinの順番を変更
    • 7.6 (D)in演算子+サブクエリ
    • 7.7 (E)in演算子+サブクエリ+with句
    • 7.8 (F)distincton
    • 7.9 (G)ウィンドウ関数
    • 7.10 (H)非等値結合
    • 7.11 まとめ
    • コラム:どの部分を高速化しているのか?

執筆環境

ここからは、原稿の執筆環境やPDF入稿や、当日の販売状況を紹介する。

原稿は、Markdown風のフォーマットで書いて、それをRe:VIEWフォーマットに変換した。これはRe:VIEWの機能がいまいちで、たとえば行コメントはあるけど範囲コメントがない、といった機能不足を補うため。そしてそれをLaTeXに変換し、PDFを生成し、それを入稿した。

(Re:VIEWは、PDFとepubの両方を作れるのが最大のメリットなのだろう。今回のようにPDFだけでいい場合は、Re:VIEWを飛ばして直接LaTeXへ変換するのでもよかった。)

中表紙や奥付は、Re:VIEWの機能を使わず、Pages.appで手書きした。それをPDFに書き出し、本文のPDFと合体させた。合体は、Preview.appを使って手作業してたけど面倒になって、次のようなスクリプトを使って自動化した。

$ cat /usr/local/bin/pdfcombine
#!/bin/sh
script="/System/Library/Automator/Combine PDF Pages.action/Contents/Resources/join.py"
python "$script" --output $@
$ pdfcombine *.pdf > tmp/book.pdf

表紙は、きれいなイラストが書けないので、仕方なく技術評論社の本のデザインを拝借し、Pages.appで描いた。見て分かるように、これだと文字だけでできるので、オライリーのデザインよりはるかに作りやすい。

ただ、拝借しておきながらこういうことをいうのもおかしいが、このデザインはとても素人くさい。明朝体とゴシック体の使い方に意図を感じないし、文字の配置も適当に並べたようにしか見えない。腕のいいデザイナーの仕事とは思えないので、編集部がデザイン代をケチったのではないかという疑惑が自分の中では渦巻いた。そして世間の本の装丁がいかによくできているかを思い知った。

LaTeXつらすぎ

今回、久しぶりにLaTeXを触ったが、つらすぎる。21世紀になってずいぶん経つのに、LaTeXのつらさは変わってないのは、人類の敗北である。

まず、環境構築がつらい。いまどきのアプリはダウンロードしてクリックするだけでインストールできるべきなのに、LaTeXは相変わらず20世紀を引きずっている。特にPDFへのフォント埋め込みのための追加設定は、何度も試行錯誤するはめになった。ぐぐってもYosemiteやMavericksやEl Capitan時代のページしかヒットしなくて、Sierraでフォント関連の設定が変わっているのに対応してなかったり。関係者は「これでもだいぶ改善されてるんだぞ!」と言うかもしれないが、改善されてこの惨状なら、一般人に普及するのは無理

それからデザインの変更がつらい。Verbatim環境(HTMLでいうところのpreタグ)の中でボールド体や下線を引くことができなかったり、ヘッダーやフッターをちょっとカスタマイズするだけのことに、四苦八苦した。特にスタイルファイルを修正してもそれが反映されないときに、なぜうまく反映されないのかがまったく分からないのが、非常に腹が立つ。原稿執筆時間の3割はLaTeXのスタイルファイルを調整することに費やされた。無駄すぎる。

あと、コンパイルにかかる時間がつらい。文章を少し修正して、コンパイルして、また修正して、コンパイルして、…と繰り返すのは、ある程度は仕方ないにしても、こうもコンパイルが遅いとイライラする。コンパイルに時間がかかるせいで、原稿執筆時に思考の流れが妨げられる。不毛すぎる。この点、Go言語は素晴らしい。

やはり、これからの時代はHTML+CSSでデザインしたい。数式を書くなら今でもLaTeXが最強だろうが、今回のような数式の出てこない本であれば、LaTeXである必要性は薄い。21世紀にもなってLaTeXに苦しむのは人類の敗北である。CSSでのページレイアウトはまだまだであるが、LaTeXよりは将来性があると信じてる。

印刷所へ入稿

今回は、技術書典開催元推薦の日光企画にお世話になった。今回の本は表紙1〜4を含めて148ページあるが、これだけのページ数に対応してくれる印刷所は案外少ないそうだ。日光企画さんだと少なくとも300ページに対応してくれている。

入稿はB5サイズのPDFで行った。漫画の同人誌と違って、トンボは必要なかった(断ち切りが必要ないからだろう)。しかし漫画の同人誌と同じように、表紙を含めて1から始まる番号(これをノンブルというそうだ)を本文につける必要があり、これはilovepdf.comというWebサービスを使った。ここにページ番号をつける機能があって、色や場所が指定できるので、目立たないよう薄いグレーでページの端にノンブルをつけた。ただしフリー版だと総ページ数に上限があったと思うので、何度もやり直すと上限に達して番号がつけられなくなったと思うので、注意。

なにぶん初めての入稿だったので、分からないことばかりで苦労した。日光企画さんに限らないけど、どの印刷所も、入稿用のフォームは初心者には分からない専門用語ばかりでまいった。ちょうど、技術者が作ったアプリケーションも一般の人から見るとこうなんだろう。日光企画の担当者さんがメールで丁寧に対応してくれたことに非常に助けられた。

ちなみに日光企画さんでは、B5版の場合、148ページを超えると締め切りが2、3日早まるそうだ。最初はそれを知らずに160ページぐらい書いてて、入稿日に値段表を見るとさりげなく「148ページを超えると締め切りが早まります」と書かれてあって驚いた。仕方ないのでLaTeXのスタイルファイルを調整して上下の余白を少なくして、本文を148ページにした。話はこれだけでは終わらず、実はこの148ページ制限には表紙1〜4も含まれることが判明。仕方ないので本文をいくらか削って、本文144ページ(表紙1〜4を含めて148ページ)に無理やり収めた。

印刷部数

印刷部数は、今回は100部にした。

本当は150部にしても値段があんまり変わらなかったので、150部にするつもりだったけど、148ページあると厚さが1cmくらいになる。それが100冊だと単純に積み重ねて1mくらい。これはさすがに現場に持ち込めないなろうと思い、仕方なく100部にした。

また厚さが1cmもあると、売れ残った在庫を持ち帰るのが大変である。もし150部印刷して50部売れ残ったとしよう。薄い本であれば50部ぐらいだろうと持ち帰れるけど、厚い本だとちょっと難しい。売れ残った在庫は宅配できるサービスが用意されているけど、初心者なのでそんな便利なサービスは知らなかった。

薄くない本はいろいろリスクが大きいのである。

また今回は開催本部のご好意で、サークルの被チェック数が確認できるようになっている。これを見れば注目度を事前に知ることができるけど、被チェック数が増えるのは前日であり、入稿はそれよりずっと早いから、参考にはならない。残念だがしょうがない。

当日

当日は予定より遅れて10:30過ぎに会場へ到着。準備したこと:

  • 百均で買ったテーブルクロスを敷く。
  • 百均で買ったコイン入れにお釣りを入れる。
  • 百均で買った値札立てに値段を書いた紙を入れる。
  • 百均で買った画用紙に100均で買ったマジックでお品書きを書く。
  • 百均で買ったイーゼル(絵を立てる道具)に画用紙を載せる。
  • 準備してると椅子がじゃまだったので、2脚のうち1脚を返却。
  • 段ボールから同人誌を取り出して机にデプロイ。
  • 見本誌を提出。
  • 両隣にあいさつ。

まさに百均さまさまである。陰のMVPといっていい。

11:00より少し遅れて開場。と同時に、真っ先に買いに来てくれた人がいてびっくりした。その後もひっきりなしに売れ続けた。机の前に人だかりができてしまい、ワンオペだったのでてんてこ舞いだった。

最初の30分で半分が売れた。このときは、中身を確認して買う人と、中身を見ずに買う人が、半々くらい。その後は売れ行きが落ちて、中身を確認しても買わない人が増えたけど、結果として13:15ぐらいに完売した。

ここまで売れ行きがいいとは思ってなかったので、驚きである。MySQLならわからなくもないけど、ポスグレの本がこんなに売れるとは信じがたい。たぶん、「(SQLが)遅いじゃないかミッターマイヤー…」というキャッチコピーがオッサン世代の心を掴んだのだろう。ロイエンタール様々である。真のMVPといっていい。

その後も問い合わせが相次いだけど、その時点では再販も何も決まってなかったので、「再販についてはTwitterでお知らせするので、よろしければフォローしてください」とお知らせしておいた。

失敗したのは、全部売ってしまったこと。初めての同人誌だから、記念となる一冊を手元に残しておけばよかった。

読書感想文

感想ツイートが2件しか見つからなかったけど、どちらも好評だったので、よしとする。

他に感想書いてる人いたら教えていただきたい。

再販とマストドン旋風

すでに、100部を150に増やしても印刷代は大して変わらない、と説明した。逆をいえば、50部に減らしても大して変わらない。再販する場合、50部で数万円はちょっと…。なので、紙の本を再販する予定はない。

技術書典2に参加が難しい地方在住の人のことを考えて、PDF販売は期間限定でやろうとは思ってた。けどイベント終わった直後に販売するつもりはなく、やるとしても半年くらい期間を置いてからしよう、そう考えてた。

ところがイベント開催直後ぐらいから、マストドンなるものが大流行した。マストドンはRails+React+ポスグレで作られているそうだ。そしてmstdn.jpの中の人が「DB遅い」と言っている。

このままでは「ポスグレは遅い、やっぱりMySQLだな」という評判になりかねないので、当初の予定を早めて、PDF版の販売を始めた。Twitterで報告したら何人か買ってくれた(感謝)。マストドン管理者がこぞって買いに来てくれるかと思ったけど、まったくそんなことはなかった。特需は発生しなかった。

お値段2500円

PDF版の値段は、驚きの2500円である(紙版はもっと安かった)。他の電子書籍と比べて明らかに高い。これには理由がある。

  • 148ページあるし、内容を考えても高い価格を正当化できること(某オラクル社のコンサルが月200万以上することを考えれば、この内容で2500円なら破格である)。
  • 売れ残りの心配をしなくていいので、高い価格のせいで売れなくても別に困らないこと(印刷代はすでに回収できている)。
  • 「紙の本よりPDFのほうが便利」という人が多い。ということは、より便利であるPDF版は、紙より値段が高くてもいいはず(原価の違いは、売る側の問題であって買う側には関係ないはず)。
  • 技術系同人誌の平均単価を上げる存在でありたい(安値競争に参加しない)ということ(これは完全に個人的な願望)。

なお値段が2500円だというのに、3000円出して買ってくれた人がいた。しかも複数人である。振込み間違いでなければ、これは「よくやった」というチップだと思い、ありがたく頂戴することにした。この恩は、よりよい次回作を書くことで報いたい。

全体の感想

  • 売れると気持ちいい。逆に、売れなかったらすごいストレスになりそう。
  • 表紙がかわいい女の子じゃなくても、100部売ることは可能。
  • 両隣のサークルがどちらも気さくだった。初参加の当機関にもフレンドリー。感謝。
  • 次も参加する(当選すれば)。薄い本は出さない。
    (完全に余談だが、オブジェクト指向Fortranの本は厚さが17mmくらいあるそうだ。当機関の本も厚いほうのはずだが、上には上がいるものだ。)