SQLer 生島勘富 のブログ

RDB・SQLの話題を中心に情報発信をしています。

粘着されるとうっとうしいので少し

 私はいろんな人に絡まれています。今までのべ数百人を相手にしてきたので、コメント下さる方はコメントを送った!という感覚かも知れないけれど、残念ながら会ったこともない人を覚えるなんてことはほとんどありません。

 1年以上前に「こんな条件でC++でJOINしたら速いんだぜ!」ってテストをしてくれたようです。今まで気づかなくて申し訳ない。

    SQLの実行パフォーマンスについて 2010

 さらに続きも書いて頂いているようなので、

    SQL(JOIN)の実行パフォーマンスについて2011
    騙されないようにする為に(適切な議論の方法)

 あまり粘着されるのも困るので少し。どうせ粘着されるなら女性がいいな……。


 データはこんな感じらしい。

 ・テーブル定義:
    価格(Price)テーブル     *レコード件数は、4,671,568件
    CREATE TABLE [dbo].[Price](
        [ID] [int] NOT NULL,
        [CODE] [nvarchar](10) NULL, -- 会社コード
        [RDate] [datetime] NULL, -- 日付
        [OPEN] [int] NULL, -- 始値
        [CLOSE] [int] NULL -- 終値
    ) ON [PRIMARY]
    
    会社情報テーブル     *レコード件数は、1950件になります。
    * CODEに一意のインデックスを張っています。
    CREATE TABLE [dbo].[Company](
        [ID] [int] NOT NULL,
        [CODE] [nvarchar](10) NULL, -- 会社コード
        [NAME] [nvarchar](50) NULL, -- 会社名
        [NumberOfIssued] [float] NULL, -- 発行済み株式数
        [ClosingDate] [nvarchar](50) NULL, -- 決算月
        [UNIT] [int] NULL -- 単位株数
    ) ON [PRIMARY]


 まあ、会社情報テーブルにサロゲートキーのIDを使うなら、価格テーブルに落とすのはCODEじゃなくて Company_IDでないと行けないんだけれど、多分、HASH JOINになるのであんまり関係ないか。

 結果は以下の様になるようです。

実験1(SQL  57秒
実験2(C++側でネステッドループ)  940秒
実験3(C++側でハッシュ)  55秒

 詳しいソースは http://www.ohfuji.name/?p=115 をご覧頂くとして、非常に興味深い結果になっています。

 なぜ、C++側でハッシュしたときの方が速くなるかというと、会社情報テーブルは全件が価格テーブルにヒットする条件になっていて、一旦、全マスターをキャッシュしてから結合しています。ご本人も書かれているとおり、JOINしたときには、会社情報の名前が平均約2000回重複して送信されるためトラフィックが多くなります。

 JOINしたときのトラフィックの多さと、C++の効率の悪さが逆転するのが2000倍ぐらいのレコード数の差のあたりにあるようです。奇しくも、今日書いた正規化・非正規化で逆転するパターンと同じ理由で、正規化・非正規化なら数倍の差で逆転します。

 CSVファイルに書き出している部分のウェートが大きそうなので、JOINの差がどれほどなのかイマイチ分からないけど、データの転送量が非常に大きな差になっても2秒(4%ぐらい)の差しか付かないというのは、SQLServer の HASH JOIN は速いんだな〜とも思う。

 これは、単純に私が何度も書いているデータベースサーバでJOINを避けたかったらマスターをキャッシュするべき。というのを1回のSQLで実現して、処理時間が逆転するレコード数を探したというような状態で、マスターをキャッシュするコストは、キャッシュしたデータを2000回ぐらい再利用しないと逆転しないということが分かる。

 まあ、Javaだともっと必要な気もするし、ネットワーク環境などにもよるので、一概には言えない。2000回というのは微妙ですけどね。

 ユーザがどんなタイミングで実行しても、全マスターがヒットして2000倍の差が付くということが予め分かっている処理って……。区分マスターとかならあり得るけどね。SQLでやるよりもC++の方が速いというのであれば、全マスターがトランザクションにヒットして(つまり、マスターの無駄な読み込みはゼロ)かつ、2000倍以上のレコード数の差がつくという条件に当たるか判断しなければ行けない。つまり、オプティマイザがやっている処理を作らないと実用では使えないでしょう。



 それにしても、多分、同じマシンでやっているからでしょうが、ネスティッドループで2000倍のレコード差があっても、20倍も差が付かないというのは C++ は思ったよりも速いな〜。