第12回 SQLアンチパターン読書会に参加しました

NSEGの「SQLアンチパターン読書会」の第12回に参加しました。

nseg.connpass.com

隔週の水曜日の夜に開催しています。だいたいいつも3〜4人くらいでこじんまりとやってますが、今回は7人でした。

一人が1〜2ページくらいを音読して、気になったことをみんなで話したりして、また次の人が読んで…を繰り返しすという感じでやってます。

今回は18章と19章を読みました。以下、その場で出た話を記憶を頼りに。

18章 インプリシットカラム(暗黙の列)

「冒頭のJOIN結果に同じカラムがあって予期しない値が取れたってのはこの前ハマった」

tmtms.hatenablog.com

「ワイルドカードは普通使わないよねぇ」

「Software Design 7月号の特集にも軽く書いたわー」

19章 リーダブルパスワード(読み取り可能パスワード)

「高いレイヤーの話になってきたので読みやすくなってきた」

「パスワードリセット用のURLは短縮URL使って短くできないかな」

「短縮URLのアルゴリズムにもよるけど、単純な短い文字列とURLのマッピングだと安全でなくなるのでは」

終了後

「DockerのコンテナIDってシェルで補完対象にならないかな」

「それ、Ubuntu の zsh だとできるで」


次回は 7/5 18:30〜 です。興味がある人は参加してみるのもいいと思います。

nseg.connpass.com

MySQLの日本語コレーション

4月にMySQLの日本語コレーションについて語り合う場に呼ばれていろいろ話を聞いてきました。すぐにブログを書こうと思ったんですが、はや2ヶ月経過…。

ときどき、自分がMySQLの文字コードに関して発表する際に、次のようなスライドをいれてるんですが、

https://image.slidesharecdn.com/mysql-2017-170520113534/95/mysql-2017-55-638.jpg

MySQL 8.0 でとうとう日本語コレーションが入ることになったのに、なんか期待してたのと違いました。

https://image.slidesharecdn.com/mysql-2017-170520113534/95/mysql-2017-58-638.jpg

で、その辺の話を聞きました(2ヶ月も経ってるのでうろ覚え)。

Q. わざわざ日本語ロケール作るんだったら日本人が扱いやすいロケールにしてほしい

utf8mb4_ja_0900_as_csはMySQLが独自に考えたものではない。Unicode規格に従っている。過去にいろいろ独自にやって失敗してきてるので、もう独自にやるのは避けたい。

ai(accent insensitive)で「ハ」=「パ」=「バ」になるのも、ci(case insensitive)で「や」=「ゃ」になるのもUnicodeに従っている。

Q. ja_0900_as_cs0900_as_cs と何が違うの?

utf8mb4_ja_0900_as_cs は日本語固有の規則に従う。CLDR参照。

長音記号「ー」の順序が前の字によって異なる。たとえば「アー」は「アイ」よりも前だが「ウー」は「ウイ」よりも後。

そんな凝ったことしてたのか…。

その他

コレーションは xml ファイルを置くことで独自に定義できるので、標準のコレーションが気に入らないのなら自分で定義すればいい。

マルチバイト文字はリコンパイルしないと組み込めないと思ってましたが、それは文字セットの話でコレーションについてはコンパイル不要とのことでした。

ちゃんとマニュアルにも書いてありました。10.4.4 Unicode 文字セットへの UCA 照合順序の追加

ja_0900_as_cs はひらがなとカタカナを区別しないが、区別したい場合のために ja_0900_as_cs_ks というコレーションを作ろうとしている。

うーん、個人的にはそこまで区別したいんだったら utf8mb4_bin でいいかな…。


MySQLは標準(Unicode)に従ってるだけなので、独自におかしなことをしているわけではないということでした。

そしてUnicodeの日本語の照合順序はJIS X 4061が元なので、日本語の扱いがおかしいと日本人がMySQLに対して言うのは「お前がゆーな!」状態でした。申し訳ありませんでした!

あと最近知ったのですが、ja_0900_as_cs の場合は、漢字の順番もちゃんとJIS順になってました。 試してみると、UTF-8の文字コード順ではなくJISコード順(音読みの順)になっていることがわかります。

mysql> select hex(s),s from ja_0900_as_cs order by s;
+--------+------+
| hex(s) | s    |
+--------+------+
| E4BA9C | 亜   |
| E4BC8A | 伊   |
| E99BA8 | 雨   |
| E6A084 | 栄   |
| E5A5A5 | 奥   |
+--------+------+

業界によっては実は結構嬉しいのかもしれません。

MySQL 5.7が何も言わずに起動できなかったのでメモ

MySQL 5.6と同じ方法でMySQL 5.7を起動しようとしたら何も言わずに黙って終了してしまって少しだけハマったのでメモ。

MySQL 5.6では次のようにして起動してました。

# /usr/local/mysql-5.6/bin/mysqld --no-defaults --user=mysql
  --basedir=/usr/local/mysql-5.6 --skip-networking --socket=/tmp/mysql56.sock
  --log-error=/tmp/my56.err > /tmp/my56.err 2>&1

log-error をつけていても、最初の数行が標準エラー出力に出ちゃうので、log-error と同じファイルにリダイレクトするようにしていました。

同じようにしてMySQL 5.7を起動してみたら、すぐに終了してしまって、しかもエラーを何も出力しません。

# /usr/local/mysql-5.7/bin/mysqld --no-defaults --user=mysql
  --basedir=/usr/local/mysql-5.7 --skip-networking --socket=/tmp/mysql57.sock
  --log-error=/tmp/my57.err > /tmp/my57.err 2>&1

結論からいうと、エラー出力のリダイレクトが不要でした。5.6 と異なり、リダイレクトしなくても余計な出力がされることはありませんでした。

log-error と同じファイルにリダイレクトすることで、ファイルの所有者が root になり、mysql ユーザー権限でエラーファイルに書き込みできず、エラーを出力することもできなくて終了していたのでした。

5.6の時は log-error のファイルオープン時のユーザーはまだ mysql になる前の root だからオープンできたけど、5.7ではファイルオープン時に既に mysql ユーザーになっているからファイルのオープンに失敗しているってことだと思います。ソースを見て確認したわけではないですけど、たぶん。

第11回 SQLアンチパターン読書会に参加しました

NSEGの「SQLアンチパターン読書会」の第11回に参加しました。遅刻して19時くらいに着きました。

nseg.connpass.com

隔週の水曜日の夜に開催しています。だいたいいつも3〜4人くらいでこじんまりとやってます。今回は4人でした。

一人が1〜2ページくらいを音読して、気になったことをみんなで話したりして、また次の人が読んで…を繰り返しすという感じでやってます。

今回は16章と17章を読みました。以下、その場で出た話を記憶を頼りに。

16章 プアマンズ・サーチエンジン(貧者のサーチエンジン)

「あるある」

「MySQL5.7からは日本語でも全文検索できるようになった。n-gram方式とインデックス方式が選択可能」

「PostgreSQL 9 では pg_bigm を使うと LIKE を普通に書いても全文検索インデックスが使用されるらしい」

「Apache Lucene は今だと Elasticsearch かも」

「検索時に自動的にインデックス作るのは面白い。けどやり過ぎな気がする」

あと、MySQL5.6の全文検索は日本語は使えないという脚注がついたのは自分のおかげだと自慢しました。

17章 スパゲッティクエリ

「SELECTの結果からRubyとかでUPDATEクエリを生成するのは時々やる」


次回は 6/21 18:30〜 です。興味がある人は参加してみるのもいいと思います。

nseg.connpass.com

第10回 SQLアンチパターン読書会に参加しました

5/24にNSEGの「SQLアンチパターン読書会」の第10回に参加しました。

nseg.connpass.com

隔週の水曜日の夜に開催しています。だいたいいつも3〜4人くらいでこじんまりとやってます。

一人が1〜2ページくらいを音読して、気になったことをみんなで話したりして、また次の人が読んで…を繰り返しすという感じでやってます。

今回は14章と15章を読みました。以下、話された内容を記憶を頼りに。

14章 アンビギュアスグループ(曖昧なグループ)

「これエラーにならないのMySQLだけじゃない?」

「MySQLも5.7からは標準でエラーになる」

「SQL 99 だと GROUP BYに書いてないカラムでも論理的に一意に求まるのならエラーにならないらしい」

「PostgreSQL でも最近はエラーにならなくなるケースがあるらしい」

「相関サブクエリむずい」

「MySQLは伝統的にサブクエリが遅い。最近はかなりマシになったらしい」

15章 ランダムセレクション

「LIMIT を使えない RDB あったのか!」

「プリペアドステートメントって “?” だけじゃなくて “:offset” みたいな書き方できたの?」

「それはたぶんPHPのクライアントサイドプリペアドステートメント。クエリの一部を書き換えるので過去に脆弱性問題があった。文字列としてクエリの一部を書き換えるのは悪手で、やるなら一段レイヤーを上げて Ruby の Sequel みたいなやりかたがいいと思う」


次回は 6/7 18:30〜 です。興味があって都合のつく人は参加してみてください。

nseg.connpass.com

MySQLユーザ会会 in 長野 を開催しました

5/13(土)に「MySQLユーザ会会 in 長野」を開催しました。

nseg.connpass.com

「会」が2つあるのは仕様です。「MySQLユーザ会」の会合で「MySQLユーザ会会」です。

開催経緯はこんなかんじです。

NSEG(長野ソフトウェア技術者グループ)というのがあって、毎月何かしらやってるんですが、その87回目イベントとして開催しました。


昼前に長野駅について、駅ビルのGitHubにめっちゃコミットしてるMen↓の近くでボーっとしてたら

@RKajiyama さんに遭遇したので、一緒に昼飯を食うことにしました。

他県から長野に来た人とお昼を食べるならやっぱり蕎麦かな…と思ったのですが、あまり長野駅近辺の蕎麦屋を知らなかったので、前に美味しいという噂を聞いたことがあった「ぼっち」という蕎麦屋に行きました。 満席でキューに並んでる途中で @mamy1326 さんも合流。 席につくまでも席についてからも結構待たされたのですが、美味しかったです。

主催者なのに遅れ気味に会場についたら、ほとんどの人はもう来ていました。 ドタキャンなし無断欠席なしで、さすが地方開催イベント(というか事前登録無しで来た人もいた)。発表者含めて参加者18名でした。

ツイートを Togetterにまとめました。


MySQLとは - @sakaik

RDBMSとは? MySQLとは? MySQLのバージョン、歴史等について話していただきました。

AWSでサクッとMySQL環境を立ち上げるデモもありました。

MySQL 8 - @RKajiyama

MySQLの中の人として、MySQLの次バージョンである MySQL 8 について話していただきました。

CTEとかWindow関数とかが実装されて、RDBMSとしてどんどんマトモになっていきますね。MySQLなのに。

MySQLの14年物のバグ #199 が直るらしいです。

yoku0825を支える技術 - @yoku0825

yoku0825とは「GMOペパボの福利厚生」で「はてなの外部API」で「bot」だそうです。

GTID, Multi Thread Slave, Defer Table Index, HANDLER ステートメント, PMM 等についての話がありました。

Bug #2 の紹介がありました。 Closed になってるのに 8.0 でバグが再発したらしいです。

Transactd PHP ORM - @bizstationcorp

TransactdはMySQLのHanderインタフェースを直接叩くことができるAPIを提供するプラグインで、Transactd PHP ORMはそのAPIを使用したPHPのORMです。

既存のORMのAPIとほぼ同じで、省メモリで高速とのこと。

Transactd も PHP ORM もオープンソースでGitHubにあります。

RubyにもTransactd APIがあるので、頑張ればORMを作れそうです。

MySQLの文字コード事情 - @tmtms

2月のMySQL Casualの発表の使いまわしです。MySQL 8 の話も少し追加しました。


懇親会は「酔来処」14名参加でした。

参加者のみなさま、発表者のみなさま、ありがとうございました。

1年に1回くらいは長野で開催したいですね。 次は松本でやるのもいいかなぁと思ったりしてます。自分は土地勘ないので誰か仕切ってくれる人がいれば…。

ZIP中のファイル名の文字化け(Ruby編)

tmtms.hatenablog.com

という記事を書きましたが、今回はRubyでZIPファイルを作る時の話を。

RubyでZIPファイルを作るには、rubyzip というライブラリを使います。

% gem install rubyzip

次のようにしてZIPにファイルを追加できます。

require 'zip'

Zip::File.open('hoge.zip', Zip::File::CREATE) do |zip|
  zip.add('いろはにほへと.txt', '/path/to/いろはにほへと.txt')
end

Zip::File#add の第一引数はZIP内に記録されるファイル名、第二引数は実際のファイルのパスです。この二つのファイル名は同じである必要はありません。

ただし、この場合はUTF-8フラグがセットされません。つまりWindowsの標準機能で開くと文字化けしてしまいます。

UTF-8フラグを立てるには次のように Zip.unicode_names = true を指定します。

require 'zip'

Zip.unicode_names = true
Zip::File.open('hoge.zip', Zip::File::CREATE) do |zip|
  zip.add('いろはにほへと.txt', '/path/to/いろはにほへと.txt')
end

このように作成されたZIPファイルは最近のWindowsで(それとたぶんMacでも)開くことができます。

ただし、パッチがあたってない Windows 7 や、もう世界のどこでも動いていないはずの古いバージョンの Windows は UTF-8 に対応していないようなので、文字化けしてしまいます。

その場合はUTF-8フラグを立てずにファイル名をシフトJIS(CP932)に変換してやればいいです。

require 'zip'

Zip::File.open('hoge.zip', Zip::File::CREATE) do |zip|
  zip.add('いろはにほへと.txt'.encode('cp932'), '/path/to/いろはにほへと.txt')
end

なお、これはMacでは文字化けします(たぶん)。Macでも文字化けしないようです。

あらかじめZIPファイルを渡す相手の環境がわかっているのであれば、それに合わせて変更するのもいいでしょう。

Webアプリであれば User-Agent を見て動きを変えるのもいいかも知れませんね。