2

私のチームは学校のプロジェクトのためにphp/MySQLWebサイトに取り組んでいます。一般的な情報(ID、名、姓など)を含むユーザーのテーブルがあります。以下のようなサンプルデータを含む質問の表もあります。この簡略化された例では、質問に対するすべての回答は数値です。

テーブルの質問:

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)

WHERE 1を追加して、forループに「AND(...)」を追加できるようにします。'1'を削除して、implode(and、array)を使用し、where if配列が空でない場合は追加できることに気付きましたが、これは同等であると考えました。そうでなければ、私はそれを簡単に変えることができます。

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

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

主な質問:クエリの方法は非効率的すぎますか?同じテーブルをそれ自体に最大で数十回または2回結合することは理想的ではないと思います(私たちがそれだけ多くの質問をすることになった場合)。私はいくつかの検索を行ったところ、私が探しているものに少し触れているように見えるこれらの2つの投稿を見つけました:

1つのクエリで複数の基準

これは、EXISTSで複数のネストされた(正しい用語?)クエリを使用します

複数の基準を持つ製品を検索する

youssef azariによるコメントの1つは、「クエリ1」「UNION」「クエリ2」の使用について言及しています。

これらのどちらかが、私がやろうとしていることに対してより良いパフォーマンスを発揮するか、より理にかなっていますか?

ボーナス質問:

簡単にするために上記を省略しましたが、実際には3つのテーブル(数値の質問、ブール値、およびテキスト用)があります。別々のテーブルを使用することにしたのは、(私が考える限り)それがそれであるか1つであるためです。異なるタイプの3つの値列を含む大きな回答テーブル。2つは常に空です。

これは私の現在のクエリビルダーで機能します-クエリの例は次のようになります

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の提案に従って、私はまともな量のデータを入力し、さまざまなクエリでいくつかのテストを実行しました。テーブルには約250人のユーザーを入れ、3つのテーブルのそれぞれに約2000人の回答を入れました。提供されたリンクは非常に有益であることがわかりました

(まだ2回以上ハイパーリンクできないため、リンクは削除されました)リンクはnnicholsの応答にあります

私が見つけたこれと同様に:

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

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

最初:EXISTSを使用する

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
...

3つの回答テーブルのそれぞれに10の条件を使用しました(結果として30のEXISTSになります)

2番目:INを使用する-同じ結果をもたらす非常に類似したアプローチ(おそらく正確に?)

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

再び30のサブクエリで。

私が試した3番目のものは上記と同じでした(30のJOINを使用)

最初の2つでEXPLAINを使用した結果は、次のとおりです。(同一)

テーブルuのプライマリクエリのタイプはALL(ユーザーテーブルは大きくありませんが、悪いです)であり、検索された行はユーザーテーブルの約2倍のサイズでした(理由はわかりません)。EXPLAINの出力内の他の各行は、関連する回答テーブルへの依存クエリであり、WHEREおよびkey = PRIMARY KEYを使用して1行のみを検索するeq_ref(good)のタイプを使用しました。全体的に悪くはない。

私が提案したクエリ(JOINing)の場合:

プライマリクエリは、実際には、最初に結合したテーブル(私の場合はAnswerBoolean)で、タイプはref(ALLよりも優れています)でした。検索された行の数は、誰もが回答した質問の数と同じでした(50の異なる質問が誰もが回答したように)(これはユーザーの数よりはるかに少なくなります)。EXPLAIN出力の追加の行ごとに、WHEREおよびkey = PRIMARY KEYを使用し、1行のみを検索するタイプeq_ref(good)のSIMPLEクエリでした。全体的にほぼ同じですが、開始乗数は小さくなります。

JOINメソッドの最後の利点の1つは、さまざまな値(n1.valueなど)で並べ替える方法を理解できる唯一の方法でした。他の2つのクエリはサブクエリを使用していたため、特定のサブクエリの値にアクセスできませんでした。order by句を追加すると、最初のクエリの追加フィールドが「usingtemporary」(「sによる順序」に必要)と「usingfilesort」(それを回避する方法がわからない)を持つように変更されました。ただし、これらの速度低下があっても、行数はまだはるかに少なく、他の2つ(私が得る限り)はorderbyを使用できません。

4

1 に答える 1

0

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

内部結合は、ほぼ確実にEXISTSに切り替えるよりも優れたパフォーマンスを発揮しますが、これも適切なテストデータセットとEXPLAINを使用して簡単にテストできます。

于 2012-04-06T19:11:04.053 に答える