2009年5月 7日

[mysql 14826] 【蛇足】Re: 【御礼】 Re: 【初歩的質問】 テーブルを結合するSQL文

KK@xxxxx です。

大変妥当に解決したので、この投稿は蛇足なのですが、
ちょうど似たようなことをしているときに、このやり取りがあったので
興味深く読ませていただきました。 さらに、気分に乗って、一言、余計なことを...


下記で、ちょっと気になるのは、asの後が、日本語になっていることで、

通るのか、ちょっと怖い気もしますが、実際にやってみたらうまく動作しました。

で、何が言いたいかといえば、以下のようなことをちょっと付け足したいのです。

(以下、私がちょうどやっていたことです。)
下記で、user_infoが中心的なテーブルで、他は、それにぶら下がっている
属性のテーブルです。 uidは各テーブルのprimary keyになっています。

SQL-A) select i.*,n.value as nickname,g.single as gender,d.single as
district,b.date as birthday from user_info i,user_attr_text_00000001
n,user_attr_single_00000003 g,user_attr_single_00000004
d,user_attr_date_00000006 b where i.uid=xxxxx(実際は番号が入っています)
and i.uid=n.uid and i.uid=g.uid and i.uid=d.uid and i.uid=b.uid

これと、以下のSQLの比較です。
SQL-B) select i.*,n.value as nickname,g.single as gender,d.single as
district,b.date as birthday from user_info i right join
user_attr_text_00000001 n using(uid) right join user_attr_single_00000003 g
using(uid) right join user_attr_single_00000004 d using(uid) right join
user_attr_date_00000006 b using(uid) where i.uid=xxxxx

これは、結合してから、ひとつ取り出すSQL-Bのほうが圧倒的に時間がかかります。
出てくる結果データ自体は、この実例では同じでした。

なお、ここでやり取りのあったケースのように、
特定の番号でuidを縛らない場合は、所要時間は同じです。 下記をご参照ください。

(SQLの意味合いは、相手のあるなしを配慮するしないがあるので、
厳密には違うでしょうが、ここで、属性のテーブルは、対応が無いものが
無かったので、結果は同じになりました。 もとの話題でも、NULLを「なし」に変えれば
似た形になるかも。(下のexplainの下を見てください。))

explain select i.*,n.value as nickname,g.single as gender,d.single as
district,b.date as birthday from user_info i,user_attr_text_00000001
n,user_attr_single_00000003 g,user_attr_single_00000004
d,user_attr_date_00000006 b where i.uid=n.uid and i.uid=g.uid and
i.uid=d.uid and i.uid=b.uid

table type possible_keys key key_len ref rows Extra
b ALL PRIMARY NULL NULL NULL 224085
i eq_ref PRIMARY PRIMARY 4 b.uid 1
g eq_ref PRIMARY PRIMARY 4 i.uid 1
d eq_ref PRIMARY PRIMARY 4 i.uid 1
n eq_ref PRIMARY PRIMARY 4 i.uid 1


explain select i.*,n.value as nickname,g.single as gender,d.single as
district,b.date as birthday from user_info i right join
user_attr_text_00000001 n using(uid) right join user_attr_single_00000003 g
using(uid) right join user_attr_single_00000004 d using(uid) right join
user_attr_date_00000006 b using(uid)

table type possible_keys key key_len ref rows Extra
b ALL NULL NULL NULL NULL 224085
d eq_ref PRIMARY PRIMARY 4 b.uid 1
g eq_ref PRIMARY PRIMARY 4 d.uid 1
n eq_ref PRIMARY PRIMARY 4 g.uid 1
i eq_ref PRIMARY PRIMARY 4 n.uid 1


(投稿にあたって、確認した結果も報告します。
もとの話のNullを「NA」に変えたもの:)
表示中の列 0 - 4 (5 合計, クエリの実行時間 0.0011 秒)
実行した SQL:
SELECT c.classname, m.yname, f1.fruitname, f2.fruitname, f3.fruitname
FROM `main` m, `classnm` c, `fruitnm` f1, `fruitnm` f2, `fruitnm` f3
WHERE m.`classno` = c.classno
AND m.favour1 = f1.fruitno
AND m.favour2 = f2.fruitno
AND m.favour3 = f3.fruitno
LIMIT 0 , 30 [ 編集 ] [ EXPLAIN で確認 ] [ PHP コードの作成 ] [ 再描画 ]

結果:
classname yname fruitname fruitname fruitname
Hana Taro StrawB Grape Orange
Hana Jiro Grape Apple Orange
Niji Sabu Orange NA NA
Niji Shiro Apple Orange NA
Sora Goro NA NA NA

Explain:
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE m ALL NULL NULL NULL NULL 5
1 SIMPLE c ALL PRIMARY NULL NULL NULL 3 Using where =>ここはちょっと気に入りませんが、数が少ないせいかな?
1 SIMPLE f1 eq_ref PRIMARY PRIMARY 4 test.m.favour1 1
1 SIMPLE f2 eq_ref PRIMARY PRIMARY 4 test.m.favour2 1
1 SIMPLE f3 eq_ref PRIMARY PRIMARY 4 test.m.favour3 1


(selectの内容が、元の質問に近い(?)形:)
表示中の列 0 - 4 (5 合計, クエリの実行時間 0.0010 秒)=>多少早くなっているのはデータがメモリに乗っているせい?
実行した SQL:
SELECT *
FROM `main` m, `classnm` c, `fruitnm` f1, `fruitnm` f2, `fruitnm` f3
WHERE m.`classno` = c.classno
AND m.favour1 = f1.fruitno
AND m.favour2 = f2.fruitno
AND m.favour3 = f3.fruitno

classno yname favour1 favour2 favour3 classno classname fruitno
fruitname fruitno fruitname fruitno fruitname
1 Taro 1 2 4 1 Hana 1 StrawB 2 Grape 4 Orange
1 Jiro 2 3 4 1 Hana 2 Grape 3 Apple 4 Orange
2 Sabu 4 0 0 2 Niji 4 Orange 0 NA 0 NA
2 Shiro 3 4 0 2 Niji 3 Apple 4 Orange 0 NA
3 Goro 0 0 0 3 Sora 0 NA 0 NA 0 NA

Explain:
実行した SQL:
EXPLAIN SELECT *
FROM `main` m, `classnm` c, `fruitnm` f1, `fruitnm` f2, `fruitnm` f3
WHERE m.`classno` = c.classno
AND m.favour1 = f1.fruitno
AND m.favour2 = f2.fruitno
AND m.favour3 = f3.fruitno [ 編集 ] [ SQL の EXPLAIN 解析をスキップ ] [ PHP
コードの作成 ]

id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE m ALL NULL NULL NULL NULL 5
1 SIMPLE c ALL PRIMARY NULL NULL NULL 3 Using where
1 SIMPLE f1 eq_ref PRIMARY PRIMARY 4 test.m.favour1 1
1 SIMPLE f2 eq_ref PRIMARY PRIMARY 4 test.m.favour2 1
1 SIMPLE f3 eq_ref PRIMARY PRIMARY 4 test.m.favour3 1

selectを*で手抜きしても良いということですかねぇ。
経験上、*にするのはちょっと怖いですね。
*にすると、余分な組み合わせも考えてくれちゃうことが多いですね。

なお、今までは私もあまりjoinを使ったことが無く、
補助テーブルやプログラムのループ、判断などで
逃げていました。 Oracleならcursorを使いたいところ?

上記でも、mainのレコードの「そのレコードになっているという組み合わせの縛り」
(うまい表現ではないですが、お許しを)をSQLに表現するのが
難しかったです。

最近は、joinも、Web検索しながら、多少使っていますが...

以上、蛇足でした。


> 結論からいいますと、ご提示のとおりで解決しました。
>
>> #ちゃんと考えると、結構難しいですよ、これ。
>
> そうとは知らず、失礼しました。
> 解法は「LEFT OUTER JOIN」でしたか・・・
> 入門本でも、理解しきれなかった部分です。
>
>
>> ということだと思うので、考え方としては園児テーブルの四角を
>> 真ん中に書いて、組みのカラムから組みマスターの四角へ線。
>
> 考え方、非常に勉強になりました。
>
>> SQL、あまり感覚で覚えられるものでもないので、まずは適当なデータ
>> ベース関連(SQLについてたくさん書かれている本)を3,4冊腰を据えて
>> 読まれると、かなりいろいろ分かってくると思いますよ^^
>
>
> 入門本に掲載されているSELECT文だけで割りと何とかなっ
> てきたので、もう少し勉強してみなければなりませんね。
>
>
>> #カラム名がたまたまみんな「name」になってしまったので、
>> #必要に応じて適宜別名をつけてください^^;
>
> こんな↓感じでよろしかったでしょうか。
>
> SELECT kumi.name AS "組名", enji.name AS "氏名", kuda1.name AS "好きな果物1",
> kuda2.name AS "好きな果物2", kuda3.name AS "好きな果物3"
> FROM kumi
> LEFT OUTER JOIN enji ON ( enji.kumi = kumi.no )
> LEFT OUTER JOIN kuda kuda1 ON ( enji.kuda1 = kuda1.no )
> LEFT OUTER JOIN kuda kuda2 ON ( enji.kuda2 = kuda2.no )
> LEFT OUTER JOIN kuda kuda3 ON ( enji.kuda3 = kuda3.no )
>
> ほぼこのまま、本番環境に適用させていただきます。
> 丁寧な解説、大変ありがとうございました。

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




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