質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
MySQL

MySQL(マイエスキューエル)は、TCX DataKonsultAB社などが開発するRDBMS(リレーショナルデータベースの管理システム)です。世界で最も人気の高いシステムで、オープンソースで開発されています。MySQLデータベースサーバは、高速性と信頼性があり、Linux、UNIX、Windowsなどの複数のプラットフォームで動作することができます。

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

Q&A

解決済

8回答

4609閲覧

WHERE句で3カラム中2カラムに一致する場合のSQL式

takepieee

総合スコア686

MySQL

MySQL(マイエスキューエル)は、TCX DataKonsultAB社などが開発するRDBMS(リレーショナルデータベースの管理システム)です。世界で最も人気の高いシステムで、オープンソースで開発されています。MySQLデータベースサーバは、高速性と信頼性があり、Linux、UNIX、Windowsなどの複数のプラットフォームで動作することができます。

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

3グッド

4クリップ

投稿2017/02/14 02:09

とある検索ページを作成中です。
その中に◯◯且つ△△というフォームがあります。
例えば◯◯と△△の趣味検索が出来るフォームがあり、趣味を3つ登録出来るテーブルがあったとし、

mysql

1SELECT name FROM address WHERE hobby1 IN (?,?) AND hobby2 IN (?,?) AND hobby3 IN (?,?)

これでは3つ中1つには趣味が一致しませんので結果を返しません。
この「3つ中2つに合致する値がある」という場合、どういった式があるのか想像がつかず、質問させていただきました。

ご教授の程お願い致します。

_26_K, ny_an, yodel👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

liguofeng29

2017/02/14 02:28

DBの種類はなんでしょうか?
takepieee

2017/02/14 02:30

MySQLです。MySQLiで作っています。
guest

回答8

0

ベストアンサー

可読性にやや難ありですが、CASEを使ったこういう書き方もできます。

sql

1SELECT 2 name 3FROM 4 address 5WHERE 6 (CASE WHEN hobby1 IN (?,?) THEN 1 ELSE 0 END) + 7 (CASE WHEN hobby2 IN (?,?) THEN 1 ELSE 0 END) + 8 (CASE WHEN hobby3 IN (?,?) THEN 1 ELSE 0 END) >= 2

投稿2017/02/14 02:29

alg

総合スコア2019

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

takepieee

2017/02/14 02:50

ありがとうございます。私の今回のケースはこちらで解決致しました。
haru666

2017/02/14 02:56

このコードだとhobby1とhobby2が両方とも同じ値で、3つ中1つに合致していてもクエリが通ってしまうので気を付けてください。 例 hobby1=1, hobby2=1, hobby3=3 という列は(1,2)で検索した時ヒット対象です CASE WHEN hobby1 IN(1,2) THEN 1 ELSE 0 END -> 1 CASE WHEN hobby2 IN(1,2) THEN 1 ELSE 0 END -> 1 CASE WHEN hobby3 IN(1,2) THEN 1 ELSE 0 END -> 0
takepieee

2017/02/14 03:04

>haru666様 hobby1, hobby2, hobby3それぞれユニーク値という想定しかしておりませんでした。ご指摘感謝致します。
guest

0

考え方として、
・hobby1 に指定した趣味があるレコードを取り出す
・hobby2 も同様
・hobby3 も同様
として、それぞれを持つレコードの一覧が出ます。
この3つの一覧の中に、2つ以上重複して出てくるレコードが欲しい結果になりますね。
なので GROUP BY と HAVING で集計してしまえそうです。

下の SQL で UNION ALL ではなく UNION を使っているのは、ないとは思いますがあるレコードで hobby1 と hobby2 とに同じ趣味を書いている場合(その他も同様)を排除するためです。この制限を考慮しなくてよいなら UNION ALL で多少早くなります。

SQL

1SELECT name 2FROM ( 3 SELECT name, hobby1 AS hobby FROM address WHERE hobby1 IN (?, ?) 4 UNION 5 SELECT name, hobby2 AS hobby FROM address WHERE hobby2 IN (?, ?) 6 UNION 7 SELECT name, hobby3 AS hobby FROM address WHERE hobby3 IN (?, ?) 8) GROUP BY name HAVING count(*) >= 2

投稿2017/02/14 02:33

tacsheaven

総合スコア13703

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yuba

2017/02/14 02:45

algさんの回答にもtacsheavenさんの回答にも+1を付けましたが、私の中でより評価の高いのはこちらになります。 どなたも直接ご指摘なさってはいないのですが、テーブル設計には問題があるのです。hobby1, hobby2, hobby3 というカラムを並べていることが問題で、これは避けるべき手法の一つとして「マルチカラムアトリビュート」という名前がついています。 [7章 マルチカラムアトリビュート(複数列属性) - Qiita](http://qiita.com/mizunokura/items/a9be12e0eddcf5d90f07) つまり長期的にはテーブル構造を改善すべきなのですが、tacsheavenさんの回答は改善後のテーブル構造をその場で再構成して、それに対して問い合わせているクエリです。ということは、テーブル構造改善後もそのまま使っていけるということになります。 この将来を考えた解決が◎と感じた次第であります。
takepieee

2017/02/14 02:46

ありがとうございます。
takepieee

2017/02/14 02:52

>yuba様 ありがとうございます。 非常に分かりやすい解説でした。
haru666

2017/02/14 02:58

欠点としては重たいクエリですし、他の条件を合わせて使いたい時には都合が悪いクエリなので、将来性は完璧だけど現行のパッチとしては今一という感じがします。
alg

2017/02/14 03:00

yubaさんのコメントに「なるほどー」と感心しました。 実業務でもhobby1から20まであるようなテーブルをたまに見かけますし、そのたびにモヤモヤしています。 が、それはそれとして受け入れてしまうしかないケースがほとんどなので、テーブル構造改善までは思い至りませんでした。 後々のテーブル構造改善まで見すえたtacsheavenさんの回答、素晴らしいですね。 それと私の回答には解説がないので、その点でもtacsheavenさんの回答の方が優れていると思います。
guest

0

これはhobbyを3カラムに分けずに、1カラムで管理し
正規化すれば一発で解決します

投稿2017/02/14 02:41

yambejp

総合スコア114814

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

takepieee

2017/02/14 02:48

確かにそうですね。php以前の問題ですね。
ikuwow

2017/02/20 04:48

他の回答で紹介されているような方法でできなくもないですが、 根本的にはやはり設計が正規化されていないのが原因ですね。
guest

0

それぞれの組み合わせで検索を行い取得したデータを結合すれば良いと思いますよ。

sql

1SELECT name FROM address WHERE hobby1 IN (?,?) AND hobby2 IN (?,?) 2UNION 3SELECT name FROM address WHERE hobby1 IN (?,?) AND hobby3 IN (?,?) 4UNION 5SELECT name FROM address WHERE hobby2 IN (?,?) AND hobby3 IN (?,?)

投稿2017/02/14 02:17

退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

takepieee

2017/02/14 02:29

ありがとうございます。 実際の検索項目は20項目程度の検索項目があり、その入力内容次第で可変プレースホルダを条件分岐で設定していますので、頭が混乱しています。。 少し頭を整理します。
guest

0

解決策1. EXISTS句を使う
利点:条件を追加しやすい
パラメータ化するとき、上下で1個目の趣味、2個目の趣味を分けてどちらかに存在することを調べます。
(例えば3つのhobbyのうち1つ一致も2つ一致も、WHERE句を連結するだけでよい)
欠点:テーブルの捜査回数が心配。正規化してインデックス張って使えば大分改善します。

SQL

1SELECT name 2FROM address T 3WHERE EXISTS(SELECT * FROM address WHERE T.id=id AND (hobby1=? OR hobby2=? OR hobby3=?)) 4AND EXISTS(SELECT * FROM address WHERE T.id=id AND (hobby1=? OR hobby2=? OR hobby3=?))

解決策2.ネストされたOR句を使う
利点:早い。
欠点:読みづらい=クエリを構築しづらい。条件の変化で構築すべきクエリがかわる。

SQL

1SELECT name 2FROM address T 3WHERE 4((hobby1=? AND (hobby2=? OR hobby3=?) OR 5(hobby2=? AND (hobby1=? OR hobby3=?) OR 6(hobby3=? AND (hobby1=? OR hobby2=?))

パラメータが?だと分かりにくいので追記

SQL

1SELECT name 2FROM address T 3WHERE 4((hobby1=@hobby1 AND (hobby2=@hobby2 OR hobby3=@hobby2) OR 5(hobby2=@hobby1 AND (hobby1=@hobby2 OR hobby3=@hobby2) OR 6(hobby3=@hobby1 AND (hobby1=@hobby2 OR hobby2=@hobby2))

投稿2017/02/14 02:42

編集2017/02/14 04:41
haru666

総合スコア1591

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

takepieee

2017/02/14 02:49

ありがとうございます。
guest

0

テキストエディターで書いたので脱字があるかも知れません。

まず、oracleの場合

sql

1-- ○○を持つ人 2SELECT name FROM address 3WHERE hobby1 = ○○ or hobby2 = ○○ or hobby3 = ○○ 4INTERSECT -- 重複する人を選ぶ 5-- △△を持つ人 6SELECT name FROM address 7WHERE hobby1 = △△ or hobby2 = △△ or hobby3 = △△

mysqlの場合、intersect実現

sql

1SELECT name from address add1 2WHERE hobby1 = ○○ or hobby2 = ○○ or hobby3 = ○○ 3EXISTS ( 4 SELECT name from address add2 5 WHERE hobby1 = △△ or hobby2 = △△ or hobby3 = △△ 6 and add2.name = add1.name 7)

もちろん、nameは主キーではないので、
主キーで結合を行うべきでしょうね。

投稿2017/02/14 02:39

liguofeng29

総合スコア801

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

下記の方針でいけると思いますよ。
1-WHERE句は [趣味1=hoge or 趣味2=hoge or 趣味3=hoge]
2-SELECT句でCASEを使い趣味1、2、3がhogeと一致するなら1にする。
3-2の値の合計が2以上の物を抽出する。

SQL

1select name from (select [2の内容] where [1の内容] from hoge) as hoge where [3の内容]

投稿2017/02/14 02:31

yona

総合スコア18155

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

takepieee

2017/02/14 02:44

ありがとうございます。
yona

2017/02/14 02:47

カラムがたくさんだとダメかもしれないです。
guest

0

本来は正規化が求められるのは置いておいて…。

yuba 様のコメントにある
7章 マルチカラムアトリビュート(複数列属性) - Qiita
を読んだのですが、

SQL

1SELECT * FROM Bugs 2WHERE 'performance' IN (tag1, tag2, tag3) 3 AND 'printing' IN (tag1, tag2, tag3);

という書き方が許されるようで、

SQL

1SELECT name FROM address WHERE ? IN (hobby1,hobby2,hobby3) AND ? IN (hobby1,hobby2,hobby3);

のようにしても実現できるかもしれませんね。無駄な処理は少なそうです。

投稿2017/02/21 06:59

takotakot

総合スコア1111

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

takepieee

2017/02/22 07:07

すごいですね。これでも行けました。 可読性も高く、プレースホルダも1/3で済みます。 測ってませんが早そうな気がします。 ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問