EXISTS
投稿日: 2012-02-13 /
カテゴリー:job
はっ、たまにはちゃんと仕事もしてるところを書いておかないと、延々神戸くん妄想してるだけだと思われてしまう!
というわけで仕事の話です。
今、MS-Accessでデータ書いたり読んだりするシステムをメンテしたりもしてるのですが、そのプログラムの接続先のデータベースが、SQLServerとmdbファイルの両方ありうる(エンドユーザによって違う)ので、SQLServerまたはAccessのmdbファイルの両方に同じSQL文で問合せをしないとならなくて、中々難儀なのです。
こないだあったのは、EXISTSを使うとmdbファイルでの実行速度が死ぬほど遅い、という現象でした。
こんな感じです。
親テーブル:tableA(主キー:ID)
子テーブル:tableB(主キー:ID、XCode)
以下の2つの条件のいずれかに該当するIDの件数を取得したい。
1. 子テーブルに、XCode=10のデータがある。
2. 子テーブルに、XCode=21のデータとXCode=22のデータが両方ある。
普通に
SELECT COUNT(*) FROM tableA WHERE ( EXISTS( SELECT * FROM tableB WHERE tableB.XCode = 10 AND tableB.ID = tableA.ID ) ) OR ( EXISTS( SELECT * FROM tableB WHERE tableB.XCode = 21 AND tableB.ID = tableA.ID ) AND EXISTS( SELECT * FROM tableB WHERE tableB.XCode = 22 AND tableB.ID = tableA.ID ) )
てな感じで行けると思ったんですよ。そして実際ちゃんと件数取ってこられてたんですよ。
でもmdbファイルだと、件数増えると死ぬほど遅くなることが判明。親テーブルの件数が5000件超えたときなんて1時間ぐらいかかってましたよ。
ちょっとこれでは辛いので、作戦を変更。
SELECT COUNT(*) FROM ((( tableA AS a LEFT JOIN ( SELECT * FROM tableB WHERE tableB.XCode = 10 ) AS b10 ON a.ID = b10.ID ) LEFT JOIN ( SELECT * FROM tableB WHERE tableB.XCode = 21 ) AS b21 ON a.ID = b21.ID ) LEFT JOIN ( SELECT * FROM tableB WHERE tableB.XCode = 22 ) AS b22 ON a.ID = b22.ID ) WHERE (b10.ID IS NOT NULL) OR ( b21.ID IS NOT NULL AND b22.ID IS NOT NULL )
ださいんだけどmdbファイルではこっちのが全然速いので仕方ないのです。
というわけで、状況によってアプローチは色々変えないとならないことがあるので、やっぱ色んな方法を知らないとですね、てのが今日の教訓。
全部が全部、スマートな環境で実行できるとは限らないのが実情です。しょんぼり。