2008年12月12日

[mysql 14709] Re: UNIONしたテーブルとJOINする際にインデックスが使われずテーブルスキャンになるのを回避する方法をご存じの方はご教示いただけますと幸いです。

こんにちは。
株式会社ビービットの玉越です。

すみません、どうも解決できたようです。

chuuken kenkouさんがおっしゃる通り、
「無条件の検索結果をUNIONで繋ぐなら、テーブルスキャンされて当然だと思います。」
でした。


ここで私は思い違いをしていました。

SELECT
...
FROM
( ... UNION ALL ... ) AS union_table
WHERE
条件

があるとき、下記の動作になると期待していました。

1. 条件に合致するもののみ取り出しつつ、UNION ALLする

しかし、実際のMySQLの実装は下記のようになっているようです。

2. 律儀にUNION ALLを全部し終わってから、条件に合致するものに絞る

1., 2.のどちらでも、集合演算として得られる結果は変わりません。
そのため、オプティマイザ、あるいは実装が1.になっているはずだと思い込んでいました。
(そのため、インデックスを使ってくれないのはなぜか、という的を外した質問になっていました)

というわけで、条件を( ... UNION ALL ... )の中に入れ、
( ... 条件 UNION ALL ... 条件 )という形にしたところ、期待通りの動作になりました。

SQL文は長くなりましたが、問題なく動作しました。
大変お騒がせしました、ありがとうございます。


2008/12/11 20:52 玉越大輝 <hiroki.tamakoshi@xxxxx>:
> こんにちは。
> 株式会社ビービットの玉越です。
>
> ご返信ありがとうございます。
>
> まず、MySQLは5.0.50です。
>
> 次に、無条件であればテーブルスキャンされて当然というご指摘は、おっしゃる通りです。
> 実際のクエリはもっと複雑な条件がつきますので、テーブルスキャンになりません。
> 実際、別々にクエリを作ってUNIONするのであればテーブルスキャンにはなりません。
>
> 具体的なデータ例も提示したいのですが、とても長くなるので少し整理させて下さい。
> (実際のSQL文は700行以上あります)
>
> 日時の引き算の演算部分はインデックスが使われないというご指摘は認識しており、そこはインデックスは使われなくて大丈夫です。
>
> 問題はそこではなく、下記のようにまとめられます。
> ・構造は似ているが異なる二つのテーブルがある
> ・その二つのテーブルとJOINしつつ、条件に合致する行を一つだけ取得する必要がある
> ・そこで、一旦二つのテーブルをまとめた抽象的なテーブルを作り、それとJOINする
> ・ところが、それをすると元のテーブルにあったインデックスを使ってくれなくなる
> ・つまり、UNIONすると、元のインデックスを見てくれなくなるのではないか?という仮説を持っています。
>
> 上記の仮説が正しいのであれば、無理ではないかなあと考えています。
>
> ご提案いただいているORDER BYとLIMITの組み合わせ、NOT EXISTSなどで取り出すというのは考えていませんでした。
> ありがとうございます。
> 自分でも考えてみます。
>
>
> 2008/12/11 20:19 chuuken kenkou <ken_ken_1962@xxxxx>:
>>
>> 「はてな」への質問は、約1年前のようですが、MySQLは4.1.22のままなのでしょうか?
>>
>> 検証するために、具体的なデータ例(ヒットすべきデータ、ヒットすべきでないデータ)を
>> 提示できませんか?
>>
>> 無条件の検索結果をUNIONで繋ぐなら、テーブルスキャンされて当然だと思います。
>> また、日時などの演算を検索条件に含めた段階で、MySQLではインデクスでの絞り込みが
>> できないと思います。
>>
>> まだ具体的に考えた訳ではないですが、「直近の1件」というのを、「ORDER BYとLIMITを
>> 組み合わせる」、「NOT EXISTSなどで取り出す」といった方法になるように感じます。
>>
>>
>>
>>
>>> Date: Thu, 11 Dec 2008 19:49:28 +0900
>>> From: hiroki.tamakoshi@xxxxx
>>> Subject: [mysql 14706] UNIONしたテーブルとJOINする際にインデックスが使われずテーブルスキャンになるのを回避する方法をご存じの方はご教示いただけますと幸いです。
>>> To: ml@xxxxx
>>>
>>> こんにちは。
>>> 株式会社ビービットの玉越です。
>>>
>>> UNIONで悩んでいます。
>>> たぶん解決不能と思うのですが、解決不能なのかどうかご存じの方はご教示いただけますと幸いです。
>>> 長文で失礼致します。
>>>
>>> ■前提
>>> 構造の異なるテーブルtableA, tableBがあります。
>>> これらとtableCをJOINする必要があります。
>>> インデックスは適切に張っています。
>>>
>>> tableA
>>> id
>>> C_id
>>> time
>>> description
>>>
>>> tableB
>>> id
>>> C_id
>>> time
>>> (←descriptionがない)
>>>
>>> tableC
>>> id
>>> time
>>>
>>> ■問題例1
>>>
>>> SELECT
>>> union_table.description
>>> FROM
>>> ( SELECT
>>> tableA.C_id,
>>> tableA.description
>>> FROM
>>> tableA
>>> UNION ALL
>>> SELECT
>>> tableB.C_id,
>>> NULL
>>> FROM
>>> tableB
>>> ) AS union_table,
>>> tableC
>>> WHERE
>>> union_table.C_id = tableC.id
>>>
>>> union_table.C_idは、tableA.C_idかtableB.C_idのどちらかです。
>>> なので、インデックスを効果的に使ってくれると期待します。
>>> ところが、この方法ではtableA, tableBをテーブルスキャンするようです。
>>>
>>> ( .. UNION ALL .. )をすると、その中までは見てくれなくなるようです。
>>>
>>> http://q.hatena.ne.jp/1198431011にも同様のことがあります。
>>>
>>> ■解決案1
>>>
>>> 上記にある通り、下記のようにすればよいようです(これから実験します)
>>>
>>> SELECT
>>> tableA.time
>>> FROM
>>> tableA,
>>> tableC
>>> WHERE
>>> tableA.C_id = tableC.id
>>> UNION ALL
>>> SELECT
>>> tableB.time
>>> FROM
>>> tableB,
>>> tableC
>>> WHERE
>>> tableB.C_id = tableC.id
>>>
>>> 一度まとめテーブルを作るのではなく、結果をそれぞれ作ってからまとめる、
>>> という方法です。
>>>
>>> ■解きたい問題
>>>
>>> 上記だけであれば問題は解決するのですが、私が行いたいのは下記です。
>>>
>>> SELECT
>>> union_table.description
>>> FROM
>>> ( SELECT
>>> tableA.C_id,
>>> tableA.time,
>>> tableA.description
>>> FROM
>>> tableA
>>> UNION ALL
>>> SELECT
>>> tableB.C_id,
>>> tableB.time,
>>> NULL
>>> FROM
>>> tableB
>>> ) AS union_table,
>>> tableC
>>> WHERE
>>> union_table.C_id = tableC.id
>>> AND tableC.time - union_table.time = (
>>> SELECT
>>> MIN( tableC2.time - union_table2.time )
>>> FROM
>>> ( SELECT
>>> tableA.C_id,
>>> tableA.time,
>>> FROM
>>> tableA
>>> UNION ALL
>>> SELECT
>>> tableB.C_id,
>>> tableB.time,
>>> FROM
>>> tableB
>>> ) AS union_table2,
>>> tableC AS tableC2
>>> WHERE
>>> union_table2.C_id = tableC.id
>>> AND tableC2.id = tableC.id
>>>
>>> tableC.timeから見て、tableAとtableBのtimeのうち、一番近いものを一つだけ持ってくる
>>> という操作です。
>>>
>>> これを解決案1のようにしてしまうと、別々に持ってきたものをUNION ALLするので、
>>> 一つだけではなく二つ持ってきてしまうことになります。
>>>
>>> 解決は無理そうと思っていますが、もし可能だよ、という情報をご存じの方がいらっしゃいましたらご教示いただけますと幸いです。
>>> また、不可能だよ、という情報ももしいただけますと、これ以上悩まなくて済むので同様にありがたいです。
>>>
>>> よろしくお願い致します。
>>>
>>>
>>> --
>>> -------------------------------------------------------
>>> ◆ビービットはチームマイナス6%に参画しています◆
>>> -------------------------------------------------------
>>> 株式会社ビービット 玉越 大輝
>>> ユーザビリティ コンサルタント
>>> beBit,Inc. Tamakoshi Hiroki hiroki.tamakoshi@xxxxx
>>> --------------------------------------------------------
>>>
>>> ◆◆9月29日(月)より下記に移転いたしました◆◆
>>>
>>> 〒102-0071 東京都千代田区富士見2-14-37 FUJIMI EAST 1F
>>> TEL: 03-5210-3891 / FAX: 03-5210-3895
>>> URL: http://www.bebit.co.jp/
>>> --------------------------------------------------------
>>> ◆◆◆お知らせ◆◆◆
>>> ・ユーザビリティ実践メモ(毎週月曜日更新)
>>> http://www.bebit.co.jp/memo/
>>>
>>> ・ビービット書籍 『ユーザ中心ウェブサイト戦略』発売中
>>> http://www.amazon.co.jp/gp/product/4797333529/
>>>
>> _________________________________________________________________
>> 「ブリーフケースからお引越し」無料25GBのファイル保存サービス
>> http://go.windowslive.jp/share/skydrive.html
>>
>
>
>
> --
> -------------------------------------------------------
> ◆ビービットはチームマイナス6%に参画しています◆
> -------------------------------------------------------
> 株式会社ビービット 玉越 大輝
> ユーザビリティ コンサルタント
> beBit,Inc. Tamakoshi Hiroki hiroki.tamakoshi@xxxxx
> --------------------------------------------------------
>
> ◆◆9月29日(月)より下記に移転いたしました◆◆
>
> 〒102-0071 東京都千代田区富士見2-14-37 FUJIMI EAST 1F
> TEL: 03-5210-3891 / FAX: 03-5210-3895
> URL: http://www.bebit.co.jp/
> --------------------------------------------------------
> ◆◆◆お知らせ◆◆◆
> ・ユーザビリティ実践メモ(毎週月曜日更新)
> http://www.bebit.co.jp/memo/
>
> ・ビービット書籍 『ユーザ中心ウェブサイト戦略』発売中
> http://www.amazon.co.jp/gp/product/4797333529/
>

--
-------------------------------------------------------
◆ビービットはチームマイナス6%に参画しています◆
-------------------------------------------------------
株式会社ビービット 玉越 大輝
ユーザビリティ コンサルタント
beBit,Inc. Tamakoshi Hiroki hiroki.tamakoshi@xxxxx
--------------------------------------------------------

◆◆9月29日(月)より下記に移転いたしました◆◆

〒102-0071 東京都千代田区富士見2-14-37 FUJIMI EAST 1F
TEL: 03-5210-3891 / FAX: 03-5210-3895
URL: http://www.bebit.co.jp/
--------------------------------------------------------
◆◆◆お知らせ◆◆◆
・ユーザビリティ実践メモ(毎週月曜日更新)
http://www.bebit.co.jp/memo/

・ビービット書籍 『ユーザ中心ウェブサイト戦略』発売中
http://www.amazon.co.jp/gp/product/4797333529/


投稿者 xml-rpc : 2008年12月12日 13:24
役に立ちました?:
過去のフィードバック 平均:(0) 総合:(0) 投票回数:(0)
本記事へのTrackback: http://hoop.euqset.org/blog/mt-tb2006.cgi/79987
トラックバック
コメント
コメントする




画像の中に見える文字を入力してください。