• ベストアンサー

紐付いている3つのテーブルに対しての検索条件の指定がうまくいきません・・。

紐付いている3つのテーブルに対しての検索条件の指定がうまく書けません。 3つのテーブルが紐付いています。 ・main_tableのIDと、note_id_tableのID ・note_id_tableのnote_IDと、note_value_tableのnote_ID main_table ID, name ----------- 1, aaa 2, bbb 3, ccc 4, ddd 5, eee note_id_table ID, note_ID ----------- 1, 1 1, 2 1, 3 1, 4 3, 5 3, 6 4, 7 4, 8 4, 9 5, 10 note_value_table note_ID, note_value ----------- 1, AAAA1 2, BBBB1 3, CCCC1 4, DDDD1 5, AAAA3 6, EEEE3 7, FFFF4 8, GGGG4 9, HHHH4 10, IIII5 検索の対象にしたいのは、note_valueです。 今まで、「note_valueに検索文字列が含まれている」main_tableのレコードを取得していました。 SELECT main_table.* FROM main_table LEFT JOIN note_id_table ON main_table.id = note_id_table.id LEFT JOIN note_value_table ON note_id_table.note_ID = note_value_table.note_ID WHERE note_value_table.note_value LIKE '%AAA%' 上記クエリだと、note_valueにAAAを含んでいるnote_ID:1,5を持つ、 main_tableのID:1,3のレコード、2件が返ってきます。 これを、検索文字列のNOT検索とAND検索に対応させたいのです。 NOT検索について、 WHERE NOT(note_value_table LIKE '%AAA%') としてみたのですが、main_tableのID:1,3,4,5のレコードが返ってきました。 期待した動作は、main_tableのID:2,4,5でした。 まず、note_value_tableにデータを持っていないmain_table.id:2はNULLのようで、 この条件では無視されてしまうようです。 また、main_table.id:1,3が返ってきてしまうのは、 main_table.id:1に紐付いているnote_IDの内、2,3,4と main_table.id:3に紐付いているnote_IDの内、6に条件が引っかかってしまうようなのです。 AND検索ですが、上記クエリの条件を WHERE note_value_table LIKE '%AAA%' AND note_value_table LIKE '%BBB%' としたところ、0件となってしまいました。 期待した動作は、note_valueにAAAとBBBを含んでいるnote_ID:1を持つ、 main_tableのID:1のレコード、この1件が返ってくることでした。 どうもこの条件だと、一つのnote_valueにAAAとBBBが含まれていないとHITしないようです。 結局JOINしているので、note_valueの数だけ同じmain_tableが前にくっついてるイメージなんですよね。 main_tableのレコード末端に、紐付いているnote_valueのフィールドを横に繋げるか、 紐付いているnote_valueの文字列を連結したものを一つのフィールドとして解釈するようなことができれば 実現できるのかなぁと思いました。 試行錯誤の末に、パフォーマンスが非常に悪いんですが、下記クエリで機能的には実現できたのですが、 これでは使えないので何かいいアイディアなどあればご教授お願いしたいです。 サブクエリを使いたくないというのがあります。 --AAAの検索 JOINする時に検索してしまうようにしてみました。 また、そのためには、note_id_tableからnote_IDを取得できていないとだめなので、 サブクエリ化してみました。 SELECT main_table.* FROM main_table LEFT JOIN note_value_table ON note_value_table.note_ID IN (SELECT note_ID FROM note_id_table WHERE main_table.id = note_id_table.id) AND note_value_table LIKE '%AAA%' WHERE note_value_table.note_value IS NOT NULL --AAAのNOT検索 WHERE条件を逆にするだけにしました。 --AAAとBBBの検索 検索条件1つに対して、JOINを増やしていくようにしました。 JOINが増えるのもそうですが、サブクエリも倍になってしまい、 検索条件を増やせば増やすほどパフォーマンスが落ちてしまいます。。 SELECT main_table.* FROM main_table LEFT JOIN note_value_table AS note_value_table1 ON note_value_table1.note_ID IN (SELECT note_ID FROM note_id_table WHERE main_table.id = note_id_table.id) AND note_value_table1 LIKE '%AAA%' LEFT JOIN note_value_table AS note_value_table2 ON note_value_table2.note_ID IN (SELECT note_ID FROM note_id_table WHERE main_table.id = note_id_table.id) AND note_value_table2 LIKE '%AAA%' WHERE note_value_table1.note_value IS NOT NULL AND note_value_table2.note_value IS NOT NULL

  • karace
  • お礼率57% (134/234)
  • MySQL
  • 回答数2
  • ありがとう数4

質問者が選んだベストアンサー

  • ベストアンサー
  • yambejp
  • ベストアンサー率51% (3827/7415)
回答No.2

#1さんの指摘もありますが、SQLを理解していないのであれば、もうすこし 基本的な論理式の考え方からきちんと学習した方がよいでしょう。 ちなみに'%AAA%'と書いている時点でインデックスが有効ではないので パフォーマンスは期待できません。 今回の件はサブクエリが使えない前提でテンポラリを使ったやり方を紹介 しておきます。 (1)note_valueに「AAA」が含まれる検索 CREATE TEMPORARY TABLE temp SELECT DISTINCT note_id_table.ID FROM note_id_table INNER JOIN note_value_table ON note_id_table.note_ID = note_value_table.note_ID AND note_value_table.note_value LIKE '%AAA%'; SELECT main_table.* FROM main_table INNER JOIN temp ON temp.ID=main_table.ID (2)note_valueに「AAA」が含まれまれない検索 CREATE TEMPORARY TABLE temp SELECT DISTINCT note_id_table.ID FROM note_id_table INNER JOIN note_value_table ON note_id_table.note_ID = note_value_table.note_ID AND note_value_table.note_value LIKE '%AAA%'; SELECT main_table.* FROM main_table LEFT JOIN temp ON temp.ID=main_table.ID WHERE temp.ID IS NULL (3)note_valueに「AAA」および「BBB」が共に含まれまれる検索 結果:1 CREATE TEMPORARY TABLE temp1 SELECT DISTINCT note_id_table.ID FROM note_id_table INNER JOIN note_value_table ON note_id_table.note_ID = note_value_table.note_ID AND note_value_table.note_value LIKE '%AAA%'; CREATE TEMPORARY TABLE temp2 SELECT DISTINCT note_id_table.ID FROM note_id_table INNER JOIN note_value_table ON note_id_table.note_ID = note_value_table.note_ID AND note_value_table.note_value LIKE '%BBB%'; SELECT main_table.* FROM main_table LEFT JOIN temp1 ON temp1.ID=main_table.ID LEFT JOIN temp2 ON temp2.ID=main_table.ID WHERE not(temp1.ID IS NULL) AND not(temp2.ID IS NULL) (4)note_valueに「AAA」、「BBB」の少なくともどちらか一方がふくまれる検索 結果:1,3 (3)の最後の2行を WHERE not(temp1.ID IS NULL) OR not(temp2.ID IS NULL) とする (5)note_valueに「AAA」「BBB」の多くともどちらか1つしか含まれない検索 結果:2,3,4,5 (3)の最後の2行を WHERE temp1.ID IS NULL OR temp2.ID IS NULL とする (6)note_valueに「AAA」「BBB」の多くともどちらか1つしか含まれない検索 結果:2,4,5 (3)の最後の2行を WHERE temp1.ID IS NULL AND temp2.ID IS NULL とする

karace
質問者

お礼

回答ありがとうございます。 質問が長文になってしまったにもかかわらず、 質問の意図を理解して更にクエリの例まであげてもらい感謝しております。 どうも私はクエリ一つで実現させようとそこにこだわりすぎてたようです。。 テンポラリテーブルを利用することは頭の片隅にもありませんでした。 条件別に例を示して頂いて大変分かりやすく本当に感謝してます。 教えていただいたものを参考に動作させてみましたが、 対象レコードが多い場合はそれでも重くなってしまったので、 テンポラリテーブル作成後、テンポラリテーブルにインデックスを貼ったところ パフォーマンス向上しました。 本当にありがとうございました。

その他の回答 (1)

回答No.1

RDBMSは、ここのカテゴリ通りMySQLですか? MySQLはバージョンにより、多くの機能追加や一部の仕様変更があるので、バージョンを明記するようにしてください。 質問内容を細かく見ていないのですが、ちょっと眺めた印象では、ONで指定する条件(結合条件)と、WHEREで指定する条件(制限条件)の違いが分かっていないようですね。 また、ANDとORの使い方も、理解されていないようです。 WHEREで指定する条件は、最終的な検索結果を絞り込む動きをします。つまり、WHEREで指定した条件に該当しないデータは、検索結果として得られません。 (内部的には、INNER JOINの動きになりますが、この辺は理解は難しいかな。。。アクセス計画を意識するレベルになったら、この辺も勉強してみてください) 一方、ONで指定する条件は、合致するデータはその行の各列値が返され、それ以外のデータはNULLが返ってきます。つまり、条件に該当しないデータも、返ってくる訳です。

karace
質問者

お礼

アドバイスありがとうございます。 使用しているmysqlのバージョンは、5.0.24です。 ご指摘通り、結合条件と制限条件の違いについて理解の欠しい部分があったかもしれません。 ただ、どうしてもクエリ一つで実現することにこだわりすぎて、 結合条件と制限条件がごちゃまぜになってしまったようです。。 質問が長文になってしまって、目を通して頂いたこと感謝致します。 ありがとうございました。

関連するQ&A

  • 複数テーブルからLIKE検索を行いたいのですが、う

    複数テーブルからLIKE検索を行いたいのですが、うまくいかないので教えてください ■やりたいこと ・一つのキーワードで、MAINテーブル「hoge」カラムと、SUBテーブル「hoge」カラムを検索して、該当したレコードを表示したい ■テーブル構成 ・MAIN … 「id」「main1」「hoge」… ・SUB… 「sub_id」「main_id」「sub1」「hoge」… ・1つの「id」に対して、対応する「sub_id」が複数(「id」1は一つだけ。対応する「sub_id」1は複数あります) ■試したこと1 SELECT a.* , r.* FROM main a LEFT JOIN sub r ON a.id = r.main_id WHERE ( a.hoge LIKE '%キーワード%' OR r.hoge LIKE '%キーワード%' ) とやると、2件ヒットするはずなのに、1レコードしか取得できません(1レコードに「hoge」カラムが2つ入ります) ■試したこと2 SELECT a.* , r.hoge as rhoge FROM main a LEFT JOIN sub r ON a.id = r.main_id WHERE ( a.hoge LIKE '%キーワード%' OR r.hoge LIKE '%キーワード%' ) とやると、2件ヒットするはずなのに、1レコードしか取得できません(1レコードに「hoge」と「rhoge」カラムになります) ■試したこと3 FROMで2箇所指定するのかと思ったのですが、 SELECT a.* , r.* FROM (main a,sub r) LEFT JOIN r ON a.id = r.main_id WHERE ( a.hoge LIKE '%キーワード%' OR r.hoge LIKE '%キーワード%' ) 結果は、#1066 - Not unique table/alias: 'r'になります ■試したこと4 LEFT JOINがいらないのかと思い、削除したら、2件ヒットするはずなのに、たくさんヒットしてしまいます ■質問 欲しいのは、キーワード検索した際、該当カラムにヒットした数だけのレコードなのですが、どうすれば良いのでしょうか? ■例 ・東京で検索 ・MAINテーブル「hoge」カラム(2レコード)で2ヒット ・SUBテーブル「hoge」カラム(3レコード)で3ヒット ・ヒットした該当5レコードを取得したい

    • ベストアンサー
    • MySQL
  • ある条件を含まないというクエリがうまく書けません。

    ある条件を含まないというクエリがうまく書けません。 test_table ID,name ----------- 1,aaa 2,bbb 3,ccc 4,ddd 5,eee test_table2 ID,value ----------- 1,100 1,200 1,300 1,400 2,100 2,200 3,900 3,800 4,400 4,500 4,600 5,100 今まで下記クエリのように、test_tableのIDとtest_table2のIDでジョインして、 test_table2のvalueに"200"を含んでいるtest_tableのレコードを取得していました。 SELECT DISTINCT test_table.* LEFT JOIN test_table2 ON test_table.ID = test_table2.ID WHERE test_table2.value = 200; (test_tableのID:1と2のレコードが返ってきます。) これを、test_table2のvalueに"200"を含まないものを返すようにしたいのです。 (test_tableのID:3と4と5のレコードを返したい) 下記クエリを作ってみましたが、ID:1と2も返ってきてしまいます。 ID:1のvalueの"100","300","400"、ID:2のvalueの"100"に条件が合ってしまうようです。。 SELECT DISTINCT test_table.* LEFT JOIN test_table2 ON test_table.ID = test_table2.ID WHERE test_table2.value != 200; なにが良い方法があればご教授下さい。

    • ベストアンサー
    • MySQL
  • SQLで他のテーブルに無いIDの抽出

    MySQLを使っています。 下記は、 1.テーブルaaaには、存在するidのものが、 2.テーブルbbbには、存在しない、 3.テーブルaaaのレコードを抽出したいものです。 "SELECT DISTINCT aaa.*" + " FROM aaa" + " WHERE (aaa.flag = false)" + " AND NOT EXISTS (SELECT * FROM bbb" + " WHERE (aaa.id = bbb.id) AND (bbb.flag = false))" ですが、実行すると、下記エラーがでます。 SQLException:Base table or view not found, message from server: \"Unknown table 'bbb' in where clause" どうすればよいのでしょうか?

    • ベストアンサー
    • MySQL
  • テーブル結合の条件指定について

    以下のようなテーブルが3つある場合に2つのテーブルにあるポイントを合計して取得したいと思っています。 ただ、外部結合しているtable_cに条件を与えるとうまく取得できません。 table_cにcdがある場合は条件を指定し、 ない場合は条件を指定しないようにするにはどうすればいいのでしょうか。 【table_a】 id    name ------------------ a1    aaa a2    bbb a3    ccc 【table_b】 b_id     a_id   user_id    point --------------------------------- b1     a1    00001    5 b2     a1    00002    2 b3     a3    00007    10 b4     a2    00356    10 【table_c】 c_id    user_id    cd     bonus_point ------------------------------------------------ c1     00001    cd_1    1 c2     00007    cd_2    1 c3     00356    cd_1    1 SELECT table_b.user_id as user_id , (ifnull(table_b.age_count) + ifnull(table_c.bonus_point, 0)) as total_point FROM table_a INNER JOIN table_b ON (table_b.a_id=table_a.a_id)    LEFT JOIN table_c ON (table_c.user_id=table_b.user_id) WHERE table_c.cd = 'cd_1' ORDER BY total_point desc ■結果 user_id    total_point ----------------------------- 00356     11 00001      6 ■求めたい結果 user_id    total_point ----------------------------- 00356     11 00001      6 00002     2 よろしくお願いします。

    • ベストアンサー
    • MySQL
  • 指定値を否定した条件で、NULLも含めた指定値以外を取得

    指定値を否定した条件で、NULLも含めた指定値以外を取得できる方法はありますでしょうか。 使用DBはMYSQLです。 test_table ID,name,value ----------- 1,aaa,100 2,bbb,NULL 3,ccc,300 4,ddd,100 5,eee,NULL SELECT * FROM test_table WHERE value = 100; valueが100のもの、ID:1,2を取得していたのですが、 100以外のものを取得する必要がでてきました。 SELECT * FROM test_table WHERE NOT(value = 100); こうすると、ID:3しか返ってきません。 valueがNULLのものも含めて取得したいのです。 NULLの値があることを考慮すれば、以下のような形で取得はできるのですが、 SELECT * FROM test_table WHERE NOT(value = 100) AND value IS NULL; value = 100という条件に、NOTをつけるかつけないかのような簡単な方法でNULLまで取得したいのです。 実はコーディング中に悩んでいるのですが、 動的ページなため、WHERE条件が定まらないのとかなりの数になります。 当初の予定では、全ての条件を()で囲んでNOTをつけるだけにするつもりでした。 これができないとなると、一つ一つ条件を見てNOTを付加+IS NULLの条件追加をしないといけなくなりそうです。 なにか良い方法がありましたらご教授願います。

    • ベストアンサー
    • MySQL
  • テーブルから複数のレコードにマッチする条件

    以下のようなSQL文があった場合 SELECT table_a.* FROM table_a LEFT JOIN table_b ON (table_a.id = table_b.aid) これでtable_bを連結したtable_aができると思いますが、table_bのフィールドは id , aid, value となっていた場合、valueが「1」かつ「2」のデータを検索する場合は、どのようにWHERE句を書けばいいのでしょうか。 WHERE (table_b.value = '1' AND table_b.value = '2') ではうまく取得出来ませんでした。 お分かりの方、よろしくお願いします。

    • ベストアンサー
    • MySQL
  • テーブル結合時のあいまい検索について

    はじめまして。Oracle9iでテーブル結合時のあいまい検索を行いたいのですが、エラーが起こってしまいます。 形としては、 SELECT TABLE1.A, TABLE1.B, TABLE1.C, TABLE2.D, TABLE2.E, TABLE2.F FROM TABLE1, TABLE2, WHERE TABLE1.A = TABLE2.D(+) というような形です。ここで、TABLE1のBとCに「%あ%」というあいまい検索をしたいのですが、上記の文に続いて、 AND TABLE1.B LIKE '%あ%' OR TABLE1.C LIKE '%あ%' と続けて実行すると、 ORA-01719: outer join operator (+) not allowed in operand of OR or IN というエラーになってしまいます。 こういった場合はどう対処すればよろしいのでしょうか?SQLについてほとんどわかりませんが、よろしくお願いします。

  • 複数のテーブルの全てのカラムを一度に検索するには?

    検索対象のカラムがid(主キー)、bc1~bc40と41個あります。 1つのテーブルに41個のカラムを作り検索させると非常に時間がかかります。 それにインデックスを全部に付けたいですが16個までしかつけられないです。 なので、5つのテーブルにカラムを分けて全部のカラムにインデックスをつけて それぞれを検索させようと思っています。 テーブルが1つだけなら SELECT * FROM `bc` WHERE `bc1`='あいうえおかき' AND `bc2`='あいうえおかき' AND `bc3`='あいうえおかき' AND ・・・ `bc39`='あいうえおかき' AND `bc40`='あいうえおかき' ORDER BY `bc33` DESC LIMIT 0,50; のようにできますが、テーブルが複数の場合はどうしたらよいのでしょうか。 検索を試す前に下記ができるか試してみました。 SELECT COUNT(*) FROM (((`ccc` INNER JOIN `bbb` ON `ccc`.`id`=`bbb`.`id`) INNER JOIN `aaa` ON `ccc`.`id`=`aaa`.`id`) INNER JOIN `ddd` ON `ccc`.`id`=`ddd`.`id`) INNER JOIN `eee` ON `ccc`.`id`=`eee`.`id` エラーにはなりませんがカウント数が0になってしまいました。 SELECT COUNT(*) FROM (`ccc` INNER JOIN `bbb` ON `ccc`.`id`=`bbb`.`id`) INNER JOIN `aaa` ON `ccc`.`id`=`aaa`.`id` これは正確にできてカウント数100000 SELECT COUNT(*) FROM ((`ccc` INNER JOIN `bbb` ON `ccc`.`id`=`bbb`.`id`) INNER JOIN `aaa` ON `ccc`.`id`=`aaa`.`id`) INNER JOIN `ddd` ON `ccc`.`id`=`ddd`.`id` 4つ目のテーブルを入れたところから無理なようです。 他に良い方法はないのでしょうか。

    • ベストアンサー
    • MySQL
  • 複数行の結果を単一列に連結(複数のテーブルを参照)

    psql (PostgreSQL) 7.3.4です。 困っています 以下のような【TABLE1】と【TABLE2】を TABLE1のt1tot2にで結合した結果を 【A.結果】のように★▲●の行を1行に表示させて取得したいのですがうまくいきません。 【TABLE1】 ID,ID_SUB, TYPE, VALUE A A1 t1 1 ★ A A1 t2 2 ★ A A1 t1 1 ▲ A A1 t2 3 ▲ B B1 t1 1 ● B B1 t2 3 ● 【TABLE2】 ID, TYPEV1 ,TYPEV2 ,Code A 1 1 AAA1 A 1 2 AAA2 ★ A 1 3 AAA3 ▲ B 1 3 BBB1 ●  TYPEV1=TABLE1のt1の値 TYPEV2=TABLE1のt2の値 【得たい結果】 ID, ID_SUB, TYPEV1, TYPEV2,Code A A1 1 2 AAA2★ A A1 1 3 AAA2▲ B B1 1 3 BBB1● 下記でTABLE1の2行を1行にまとめることができましたが、 問題のTABLE2との連結方法が分かりません。 select TABLE1.ID , TABLE1.ID_SUB , TABLE1.VALUE as t1 , (SELECT tbl1.VALUE FROM TABLE1 tbl1 where tbl1.TYPE=t2 and (TABLE1.ID , TABLE1.ID_SUB)=(tbl1.ID , tbl1.ID_SUB)) as t2 from TABLE1 where TABLE1.TYPE = t1 【結果】 ID ,ID_SUB , t1  ,t2 A A1 1 2 ★ A A1 1 3 ▲ B B1 1 3 ● 何か良い方法はありますでしょうか?

  • mysqlで横方向ではなく縦方向の条件抽出は…

    id   hoge   num 01   AAA   0 01   BBB   1 02   AAA   1 03   BBB   0 というテーブルがあったとします。 select id from table where hoge='AAA' and num=0 のような条件で検索するのは普通ですが、 カラムではなくレコード単位で見て hogeの値がAAAとBBBの両方である場合のデータとして「01」というidのみを抽出する方法はないものでしょうか。 よろしくお願いします。

    • ベストアンサー
    • MySQL