2012-04-06 12 views
2

私のチームは、学校プロジェクトのためにPHP/MySQLのWebサイトで作業しています。私は典型的な情報(ID、名、姓など)を持つユーザーのテーブルを持っています。私はまた、以下のようなサンプルデータで質問のテーブルを持っています。この簡単な例では、質問に対するすべての回答が数値です。MySQLは複数の基準でユーザーを選択します

表の質問:

qid | questionText 
1 | 'favorite number' 
2 | 'gpa' 
3 | 'number of years doing ...' 

など

ユーザーはこれらの質問のいずれかまたは全てにお答えするためのフォームに必要事項を記入の能力を持つことになります。注:ユーザーはすべての質問に回答する必要はなく、質問自体は将来変更される可能性があります。

回答テーブルは次のようになります。

表の回答:今すぐ

uid | qid | value 
37 | 1 | 42 
37 | 2 | 3.5 
38 | 2 | 3.6 

など

、私はサイトの検索ページに取り組んでいます。私は、ユーザーが検索したい基準を選択するようにしたいと思います。私は何か働いているが、それが効率的であるかどうか、あるいは規模が変わるかどうかはわかりません(私が言ったように、学校のプロジェクトです)。たとえば、お気に入り番号が100〜200で、GPAが2.0を超えるすべてのユーザーを表示することができます。現在、私は動作するクエリービルダーを持っています(正確な結果を返す有効なクエリーを作成します - 私が知る限り)。この例のクエリビルダの結果は次のようになります。

SELECT u.ID, u.name (etc) 
FROM User u 
JOIN Answer a1 ON u.ID=a1.uid 
JOIN Answer a2 ON u.ID=a2.uid 
WHERE 1 
AND (a1.qid=1 AND a1.value>100 AND a1.value<200) 
AND (a2.qid=2 AND a2.value>2.0) 

内のループのために、私はちょうど「(...)AND」を追加できるように、私はWHERE 1を追加します。私は '1'を落として、implode(とarray)を使用して、配列が空ではない場所を追加することができることを理解していますが、これは同等です。もしそうでなければ、私はそれを簡単に変えることができます。

ご覧のとおり、検索者が求めるすべての条件にJOINを追加します。これにより、a1.value ASC、またはa2.valueなどで注文することもできます。

最初の質問: このテーブル構成は少なくとも多少まともですか?質問の数は可変であり、すべてのユーザーがすべての質問に答えるわけではないので、このようなことが必要であると考えました。

主な質問: クエリの方法が非効率的すぎますか?私は、おそらく数十回または二回(同じ数多くの質問を入れてしまうと)同じテーブルに参加することは理想的ではないと思います。私はいくつかの検索を行なったし、私が探しているものを上のタッチの種類に思えるこれらの2つのポストを見つけました:

Mutiple criteria in 1 query

これは、複数のネストされた使用クエリで

Search for products with multiple criteria

をEXISTS(正しい用語を?)

ユーセフazariのコメントの一つは、これらのいずれかがより良い/私は何をしようとしているため、より理にかなって行う

う「クエリ1」UNION「クエリー2」を使用して言及?

ボーナス質問:私のようにしたので、(限り 別々のテーブルを持っていることを決定し、私は簡単のため、上記取り残さ、私は実際には3つのテーブルを持っている(数大切な質問、ブール値のため、およびテキスト)

考えることができる)それはどちらかであるか、または2つの常時空の3つの値の異なる列を持つ1つの大きな回答テーブルを持つでしょう。

これは私の現在のクエリビルダで動作します - 例のクエリは、念頭に置いて

SELECT u.ID,... 
FROM User u 
JOIN AnswerBool b1 ON u.ID=b1.uid 
JOIN AnswerNum n1 ON u.ID=n1.uid 
JOIN AnswerText t1 ON u.ID=t1.uid 
WHERE 1 
AND (b1.qid=1 AND b1.value=true) 
AND (n1.qid=16 AND n1.value<999) 
AND (t1.qid=23 AND t1.value LIKE '...') 

だろう、私の結果を取得するための最良の方法は何ですか?

最終的な文脈の一部: これは学校プロジェクトのためのものです。これが真実だが、最終的な目標(それは学部の上級設計プロジェクトである)は、部門がシニアデザインのチームを作る学生のために私たちのサイトを使用するようにすることです。サイズの大まかな見積もりの​​ために、学期ごとに、部署はおよそ200人程度のどこかの学生が私たちのサイトを使ってチームを作ります。明らかに、私たちが完了すると、当局はセキュリティ上の問題や心配する必要のあるもの(FERPAとは何か)について私たちのサイトを(うまくいけば)チェックします。私たちはすべての一般的なセキュリティ慣行とスケーラビリティの懸念を考慮に入れようとしていますが、最終的には、他者によってコードが改善される可能性があります。 nnicholsの提案を1として

UPDATE 、私はデータのまともな量に入れ、別のクエリにいくつかのテストを実行しました。私は約250人のユーザーをテーブルに入れ、約2000人の回答を3つのテーブルのそれぞれに入れました。私は

提供されたリンクは非常に有益た(私はまだ二回以上にハイパーリンクすることはできませんので、リンクは削除)リンクnnicholsの応答

ならびに私が見つけたこの1、次のとおりです。

http://phpmaster.com/using-explain-to-write-better-mysql-queries/

私は3種類のクエリを試しましたが、結局、私が提案したものが最も効果的でした。

まず:使用して、私は(EXISTS 30その結果)3つの解答テーブルのそれぞれに10点の条件を使用

SELECT u.ID,... 
FROM User u WHERE 1 
AND EXISTS 
    (SELECT * FROM AnswerNumber 
    WHERE uid=u.ID AND qid=# AND value>#) -- or any condition on value 
AND EXISTS 
    (SELECT * FROM AnswerNumber 
    WHERE uid=u.ID AND qid=another # AND some_condition(value)) 
AND EXISTS 
    (SELECT * FROM AnswerText 
... 

をEXISTS

第二:Inを使用して - 非常によく似たアプローチを(多分正確に?同じ結果をもたらす)

SELECT u.ID,... 
FROM User u WHERE 1 
AND (u.ID) IN (SELECT uid FROM AnswerNumber WHERE qid=# AND ...) 
... 

さらに30のサブクエリ。

上述したように、私が試した第三の一つ(30 JOINを使用)と同じであった

次のように最初の二つにEXPLAIN使用した結果は以下の通りであった:(同一)

表Uに次照会ALLのタイプ(悪いですが、usersテーブルは巨大ではありません)を検索し、検索された行はuserテーブルの約2倍のサイズでした(理由はわかりません)。 EXPLAINの出力内の他の行は、関連する回答テーブルの従属クエリで、タイプはeq_ref(good)で、WHEREとkey = PRIMARY KEYを使用し、1行しか検索しませんでした。全体的に悪くない。私が提案したクエリの場合

(参加):

主なクエリは、REF(ALLよりも良い)の種類と(私の場合AnswerBooleanに)最初の入社どんなテーブルの上に実際にいました。検索された行の数は誰からも回答された質問の数と同じでした(50人の異なる質問には誰でも回答しました)(これはユーザー数よりもはるかに少ないでしょう)。 EXPLAIN出力の追加行ごとに、WHEREとkey = PRIMARY KEYを使用し、1行のみを検索するだけで、タイプeq_ref(good)の単純な照会でした。全体的にはほぼ同じですが、より小さな開始乗数です。

JOINメソッドの最終的な利点:さまざまな値(n1.valueなど)で並べ替える方法はわかりました。他の2つのクエリはサブクエリを使用していたので、特定のサブクエリの値にアクセスできませんでした。 order by句を追加すると、最初のクエリの余分なフィールドが '一時的なものを使用する'(必要と思われる、私は注文のために)、 'filesortを使う'(避ける方法はわからない)に変更されました。しかし、それらのスローダウンでさえ、行の数は依然としてずっと少なく、他の2つは(私が得る限り)順序を使用することはできません。

+0

私が言及しておいたほうがいいでしょう: すべての回答テーブルで、主キーはuidとqidです。ユーザーは質問に回答するか、質問の既存の回答を更新できます。特定のユーザーの質問に複数のエントリはありません。 –

+0

質問表は動的か固定ですか?言い換えれば、それは拡大するか、新しい質問が後で追加されるでしょうか? –

+0

新しい質問が追加されたり、削除されたりします。 –

答えて

0

適切な大きさのテストデータセットとEXPLAINおよび/またはthe profilerを使用して、これらの質問のほとんどにお答えすることができます。

あなたのINNER JOINはEXISTSに切り替えるよりも優れた性能を発揮しますが、これは適切なテストデータセットとEXPLAINで簡単にテストできます。

+0

入力いただきありがとうございます!私はEXPLAINを見たことがありますが、決してプロフィールを表示しません。私はテストするためにいくつかの迷惑データを生成するために取り組んでいます。そして、テスト自体に慣れるでしょう。このタイプの検索はかなり一般的なようです。そのため、クエリを実行する標準的な方法があるかどうか疑問に思っていました。パフォーマンスのためにこれらの方法と他の方法をテストします。このタイプのクエリに対して、試して真のベストメソッドがあるかどうかを尋ねていました。 –

+0

私は最終的にいくつかのテストを実行しました - 私は質問に情報を入れます –

+0

あなたの所見を投稿する時間を取ってくれました。 3つの異なるクエリのクエリ時間はどのくらいですか?これらの小さなデータセットではパフォーマンス上のペナルティはほとんど発生しませんが、ファイルフィールドを削除できないかどうかを確認するために、値フィールドにインデックスを付けてテストすることをお勧めします。私はこれが役に立つ学習練習であることが証明されたことを願っています。 – nnichols

関連する問題