MySQL 5.6.5 から導入されたクエリ最適化戦略のMATERIALIZED について

新しいMySQL ではEXPLAIN にMATERIALIZED が出てくるようになったのに気付きました。

|  2 | MATERIALIZED | city   | ALL    | CountryCode   | NULL   | NULL    | NULL | 4188 | Using where |

なんじゃこりゃ。

サブクエリ最適化の一種

http://dev.mysql.com/doc/refman/5.6/en/explain-output.html
EXPLAIN で表示されるこれはMaterialized subquery のことだと書いてあります。

http://dev.mysql.com/doc/refman/5.6/en/subquery-optimization.html#subquery-materialization
んで、Materialized subquery はサブクエリ最適化の一種とのこと。
今日は、ここの記事に書いてあることを元に、具体例も付けて紹介してみたいと思います。

これまでの方式

これまでは

SELECT * FROM t1 WHERE t1.a IN (SELECT t2.b FROM t2 WHERE where_condition);

を実行すると、以下のように絞り込み条件t1.a = t2.b を追加する書き換えが行われて、サブクエリ側で全件取得しなくてもいいような工夫がなされていました。

SELECT * FROM t1 WHERE EXISTS (SELECT t2.b FROM t2 WHERE where_condition AND t1.a=t2.b);

本当にそうなるか、試してみます。
なおMySQL のドキュメントページで配布されているサンプルデータベースを元に実験します。
http://dev.mysql.com/doc/index-other.html

例)人口が100万人以上の都市を持つ国を取得. MySQL 5.5.27 で確認

mysql> EXPLAIN SELECT * FROM Country
    -> WHERE Code IN (SELECT CountryCode FROM City WHERE Population > 1000000);
+----+--------------------+---------+----------------+---------------+-------------+---------+------+------+-------------+
| id | select_type        | table   | type           | possible_keys | key         | key_len | ref  | rows | Extra       |
+----+--------------------+---------+----------------+---------------+-------------+---------+------+------+-------------+
|  1 | PRIMARY            | Country | ALL            | NULL          | NULL        | NULL    | NULL |  245 | Using where |
|  2 | DEPENDENT SUBQUERY | City    | index_subquery | CountryCode   | CountryCode | 3       | func |    8 | Using where |
+----+--------------------+---------+----------------+---------------+-------------+---------+------+------+-------------+
2 rows in set (0.08 sec)

City テーブルはインデックスを使った検索となっているので、明らかに

mysql> EXPLAIN SELECT * FROM Country
    -> WHERE EXISTS (SELECT CountryCode FROM City WHERE Population > 1000000 AND Country.Code = City.CountryCode);

のようにクエリが書き直された時の動きになっています。
今回はストレージエンジンから取得する行数の見積もりは245 + (245 * 8) = 約2250行ですね。

MySQL 5.6.5 からのMATERIALIZED

さて、ドキュメントの内容に戻ると、MySQL 5.6.5 からは最適化戦略として"サブクエリの実体化(Materialize)" が追加されました。
何が実体化なのかというと、サブクエリを実行するのに有利になるようなテンポラリテーブルが作られることを指しており、通常はメモリ上にハッシュインデックスが作られるようです。

この方式のメリットは、

  • クエリの書き換え不要
  • 再度サブクエリの実行が必要な時には、テンポラリテーブルを参照するだけでよい。これはインデックスを使って高速に動作。

です。

例)先ほどと同じ例をMySQL 5.6.16 で確認

+----+--------------+-------------+--------+---------------+------------+---------+--------------------+------+-------------+
| id | select_type  | table       | type   | possible_keys | key        | key_len | ref                | rows | Extra       |
+----+--------------+-------------+--------+---------------+------------+---------+--------------------+------+-------------+
|  1 | SIMPLE       | country     | ALL    | PRIMARY       | NULL       | NULL    | NULL               |  239 | Using where |
|  1 | SIMPLE       | <subquery2> | eq_ref | <auto_key>    | <auto_key> | 3       | world.country.Code |    1 | NULL        |
|  2 | MATERIALIZED | city        | ALL    | CountryCode   | NULL       | NULL    | NULL               | 4188 | Using where |
+----+--------------+-------------+--------+---------------+------------+---------+--------------------+------+-------------+
3 rows in set (0.00 sec)

ばっちりMATERIALIZED と出ています。

このEXPLAIN からは、まずid=2 のCity テーブルが全件スキャンされテンポラリテーブルとして実体化されたあとで、id=1 のcountry、(これがテンポラリテーブル) がJOIN(NLJ) を構成するというように読めます。


なおNLJ など、そもそもEXPLAIN については「漢のコンピュータ道」の記事を参照になさってください。(いつも大変お世話になっております)
http://nippondanji.blogspot.jp/2009/03/mysqlexplain.html


最初に4188件を全件スキャンしてインデックス付きのテンポラリテーブルを作る処理がいかほどのものか気になりますが、一度それができてしまえば、クエリ本体の処理に必要なアクセス行数の見積もりは 239 + (239 * 1)...いえ、 へはインメモリのハッシュインデックスで超高速に動くはずですからほぼ無視できる形で、239 + (239 * 0) = 239行 の見積もりとなるのだと考えています。

[Perl][正規表現][NoEditor(鬼車)] alt 指定のないimg タグを洗い出す方法

こんにちは、harupiyo です。

正規表現を書く時に、中にalt を含まないimg タグを探すにはどうしたらよいのだろう?と思って調べてみました。
Perl の場合と、正規表現ライブラリ"鬼車" を使っているNoEditor の場合で試してみています。


詳説 正規表現 第3版

詳説 正規表現 第3版

(ひさしぶりに読んだ...)


正規表現のパターンの中に、(?!alt)と記述すると、altを含まない場合という指定ができます。

Perl の場合

#!/usr/bin/perl

# 検索対象文字列
$str = <<EOF;
<img src="test.jpg"><img src="test2.jpg" alt="hello"><img src="test3.jpg">
<img src="test.jpg" alt="">                       
<img src="test.jpg" alt="test">
<img src="test.jpg" ALT="test">
EOF

$regexp = qr/
    (
        <img
            (?!         # 先読み条件指定開始,
                [^>]*       # > 以外の文字が続いて,
                alt="       # alt=" が来て
                [^"]        # すぐに" で閉じるのでない場合
            )           # はマッチ対象から除外する
            [^>]*       # 除外しない場合は、<img のあとに続く
        >               # > までの間の文字を
    )                   # $1 に保存する
/ix;                    # Option: i ... 大文字小文字を区別しない           
                        #       : x ... 正規表現中に# でのコメントをゆるす

# 上の正規表現は、こう一行で書いたのと同じです(どちらでも動きます)
$regexp = qr/(<img(?![^>]*alt="[^"])[^>]*>)/ix;                 

# マッチするパターンを全て表示(洗い出し)
while( $str =~ m/$regexp/g ){
    print $1 . "\n";
}

__END__
実行結果:
<img src="test.jpg">
<img src="test3.jpg">
<img src="test.jpg" alt="">

上のPerl のコードでは、正規表現にコメントを付けておきました。

NoEditor の場合

NoEditor(あるいはNoEditor 付属のYokkaGrep) でも同じことが実行できました。


以上、ご参考になさってください。

Eclipse でworkspace の存在が悪さをすることがある件

一般的な状況ではないでしょうが、こんなことがあったよということでメモ。

Eclipse のplugins フォルダの中の、不要物を削除しよう…といじっているうちに、肝心のSubversive が動かなくなってしまいました。

※こんなエラーが...SVN Repository の画面を開くところで

Could not create the view: Plug-in org.eclipse.team.svn.ui was unable to load class org.eclipse.team.svn.ui.repository.browser.RepositoryBrowser.

こわいよ!


どうしようもなくなってしまったので、まっさらな状態から始めようと新規にEclipse をインストールし、そこにSubversive プラグインをインストールして見てもやっぱりエラーになってしまう。

Windowsレジストリに登録された情報が悪さをしているのかな?と思って、

eclipse.exe -clean

として実行してみるもやはり同じところでエラー。

そこまで来てEclipse の起動時に、旧来のworkspace を指していたことを思い出し、別のところを指すようにしてみたところようやくSVN Repository がちゃんと開くようになり、問題が解決できました。

教訓

改めてworkspace を見ると、その中の.metadata\.plugins フォルダの中で各プラグインの情報を持っているようで、勝手にeclipse\plugins 下をいじってはダメだったかもしれませんね。
また、Eclipse はworkspace 下に設定情報を持っているのだ!ということでメモ。

[perl] 繰り返し演算子x のこんな動き

さあて、クイズです。

#!/usr/bin/env perl

sub counter_gen{
	my $c = 1;
	return sub { return $c++; }
}

$c = counter_gen();
say x $c->();		# (1)実行すると1 と表示
say x $c->();		# (2)実行すると2 と表示
say $c->() x 2;		# (3)実行すると33 と表示
$c->() x 2;		# (4)
say $c->();		# (?) - さあ、ここでは何が表示されるでしょう??

答え

(3)は$c->() の評価結果である3 が、繰り返し演算子x によって'33' となるのはご存知でしょうが、
(4) の部分では、なんと$c->() の評価が繰り返される動きになるようです??

結果、(?) の部分は 5 ではなく 6 ということになります。

うーむ、これは知らなかった。

…とおもったらあらら!!(後日記)

ハテブにいただいていたコメントに、色々指摘をいただいておりました。

  • hirafooさん…use strict; しないコードの動き。つか$cが
  • fbisさん…何か色々おかしい気が。Deparseした結果が見たい。
  • kitsさん…試したところ最後の結果は5だった。/ 、say x $c->() は use warnings; では警告が出る。

あわててコードを見ると、どうも書き換え途中の動かないコードを上げていたようです。これはひどい。。。
その上、use strict; use warnings; もつけ忘れていますね。

大変申し訳ない!
気を取り直して、改めて書き直したものは以下のとおり。

#!/usr/bin/env perl
use strict;
use warnings;

sub counter_gen{
        my $c = 1;
        return sub { return $c++; }
}

my $cg = counter_gen();
print $cg->() . "\n";      # (1)実行すると1 と表示
print $cg->() . "\n";      # (2)実行すると2 と表示
print $cg->() x 2 . "\n";  # (3)実行すると33 と表示
$cg->() x 2;               # (4)
print $cg->() . "\n";      # (?) - さあ、ここでは何が表示されるでしょう??

そして、実行結果は...

Useless use of repeat (x) in void context at test.pl line 14.
1
2
33
5

肝心の(?) の部分は、最初に私が書いた結果とは異なり、6ではなく5 になっています。
そして(4) のところでは警告になっていますね。

なるほど、ここまで来てやっと腑に落ちました。

わざわざ指摘をくださったみなさん、ありがとうございました!

タイル型ウィンドウマネージャ続報

タイル型ウィンドウマネージャという古くて新しい風 の続報として、しばらく使ってみた結果の報告です。

HashTWM ⇒ dwm-win32

最初に選んだHashTWM はポップアップウインドウにあるボタンが上手くクリックできないなど、致命的な問題があることがわかりました。

そこで、最初は敬遠したdwm-win32 使ってみるとウィンドウ切り替え速度も実に軽快でいい感じ!

…ところが、使っているうちに落ちたりして、安定感はいま一歩。
(配布のものはalpha1,2 とあるから、そもそもまだ安定版ではなかったのかな。)

結論

Windows のタイル型ウィンドウマネージャーはまだ不遇な感じだなと思いました。

(Ubuntuxmonad を使って幸せになっている今日このごろ。キーアサインも前回紹介したHashTWM のものと基本はいっしょです。)

世の中には、「タイル型ウィンドウマネージャ」というのがあるらしい。

GUI のウィンドウ操作は、基本的にマウスでやるものだと思っていました。ウィンドウの移動から、リサイズまで…。しかし、世の中は広い。

ウィンドウの配置を半自動化し、キー操作であれこれ指定できるのがあるようです。

しかも推進委員会というのまである!

なんだこれ〜!?と思って読んでみたら、軽快そうでいける感じ。

とりあえずWindows 用としてHashTWM(http://dockbox.demonastery.org/ からダウンロードできます)を使ってみることにしました。

試してみながら、添付のreadme.txt の操作説明の部分だけを意訳してみましたので貼り付けておきます。

※以下で出てくるMod キーは、Ctrl + Alt を指します。

Mod + Escape - Exit (Windows should be restored to normal)
HashBoxを終了します。(ウィンドウは通常表示に戻ります)

Mod + j/k - Cycle through tiled windows
別のウィンドウを選択

Mod + Shift + j/k - Move selected window down/up
選択したウィンドウの表示位置を変更

Mod + Enter - Switch selected window in to main area
選択したウィンドウをメインエリアに移動
※メインエリアとは分割枠のうち、広い方のエリアを指します

Mod + h/l - Shift split offset
縦に分割されたウィンドウの表示領域を左右に調整

Mod + z/x - Increase/Decrease number of windows in main area
メインエリアに表示するウィンドウを追加/削除

Mod + c - Close foreground window
選択しているウインドウを閉じる

Mod + Space - Switch between tiling modes, Vertical/Horizontal stack, Grid, and Fullscreen
縦、横、フルスクリーンの各タイル表示モードの切り替え

Mod + y - Display foreground window class (For use with -i parameter)
選択しているウィンドウのクラス名を表示(コマンドラインオプションの-i パラメータで指定するために使う)

Mod + u - Toggle lock cursor mode. In this mode cursor is locked inside the active window
カーソルロックモードの切り替え。
カーソルロックをすると、選択中のウィンドウ内でしか、マウスカーソルを移動できなくなります。

Mod + i - Toggle ignore mode. If ignore is on then new windows will not be tiled but will appear as normal
イグノア(無視)モードの切り替え。
イグノアモードで新しいウィンドウを開くと、タイリングされず通常のウィンドウとして表示されます。

Mod + o - Force Tile Foreground window
(タイリングされていない通常ウィンドウを)強制的にタイリングします

Mod + p - Force Untile Foreground window (useful in combination with above for moving windows to another monitor)
タイリングされていない通常ウィンドウの状態に戻します。(ダブルディスプレイなどで、別のモニターにウィンドウを移動するときに便利です)

Mod + 1-5 - Switch to tag
1〜5で指定したタグに切り替え
※タグとは仮想ディスプレイを指します。5つまでウィンドウの環境を持てます。
※指定したタグにウィンドウがなければ切り替えません

Mod + Shift + 1-5 - Move Window to tag
選択したウィンドウを指定したタグに移動

きっかけ

私はこれまで特に問題意識もなくフルスクリーン派を続けて来ていますが、そんな私にも少しだけ不満がありました。
それは、参照する記事やソースを見ながらテキストエディタで作業する等の、いわば"参照系のお仕事" の場合。

こういうときは、おもむろにWindows のタスクバーから右クリックして「上下に並べて表示」や「左右に並べて表示」を選ぶと、自動的にウィンドウが縦/横に配置されていい感じになります。
しかし、開いている全てのウィンドウが配置されてしまうので、参照する必要のないウィンドウまで出てきて邪魔でしょうがない。
表示したくないウィンドウをタスクトレイに戻し、改めて「上下に並べて表示」を選びなおすことになります。

せっかくいい機能だと思うのに、機能として片手落ちだよなぁ...

なんとかならないかな…と思っていろいろ探していたら、これを見つけました。

デメリットはまだ未知数

今のところですが、Launchy というコマンド型アプリランチャとの相性が悪く、Launchy のウィンドウが表示した瞬間に消えてしまう問題が挙げられます。

まとめ

しかし、この操作感は確かにGNU ScreenVIM のウィンドウ分割の感じに似ていて、キーボードでポン、ポンと軽快です。
自分の中ですこしづつ、新しい風が吹いて来たのを感じています。

ウワサのGoogle 日本語入力も試してみたい!

私は英語キーボードを使っているので[全角/半角]キーがなく、日本語入力にはいつもShift+Spaceキーにその機能を割り当てて使っています。
Google 日本語入力をインストールしてみて、これにもShift+Space(あるいは、あなたのお好みの) キーに[全角/半角] を割り当てる方法がないか試してしてみました。

Windows の場合

Google 日本語入力バーにある[ツール]を左クリックし、[プロパティ]-[一般]タブの、キー設定の選択から「カスタム」を選択。
さらに[編集]を押し、出てくるメニューの中から入力キーが"Shift Space" になっている所を探す。(スクロールバーで中央あたりにある)
ここのコマンドを"ToggleAlphanumericMode" に切り替えるとOKです。
さらに、今度は+キーを押して、モードを"Composition"、入力キーを"Shift Space"、コマンドを"ToggleAlphanumericMode" とします。
これでIME入力中にも全角/半角切替ができるようになります。

Mac の場合

(TODO あとで調べます〜〜)

違和感

ここまで試して気づいたのですが、このやり方ではまだIME自身のON/OFF にはなっていません。
あくまでIMEがON のまま、"ひらがな"と"半角英数"がトグルするだけです。なので、英数を打っている間もIME経由での入力になり、非常に気持ち悪いです。(慣れの問題もありますが…)
しかも、IMEがOFFの場合は、マウスで"ひらがな" 等を選択し、IME をON にしなければなりません!!

本当はIME自体のON/OFFを切り替える方法があればベストですよね。