2012-04-06 49 views
2

我的團隊在一個php/MySQL網站上爲一個學校項目工作。我有一張具有典型信息(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) 

我添加了WHERE 1所以,在for循環中,我可以再補充「AND(......)」。我意識到我可以刪除'1',只是使用implode(和數組),並添加如果數組不是空的,但我認爲這是等價的。如果沒有,我可以改變那麼簡單。

正如您所看到的,我爲搜索者要求的每個條件添加了JOIN。這也允許我通過a1.value ASC或a2.value等來訂購。

第一個問題: 這個表組織是否至少有點體面?我們認爲,由於問題的數量是可變的,並不是每個用戶都回答每個問題,所以這樣的問題是必要的。

主要問題: 查詢方式效率太低了嗎?我認爲將自己的桌子加入自己可能需要一兩次(如果我們最終提出這麼多問題的話)是不理想的。我做了一些搜索,發現這兩個職位,這似乎那種淡淡的什麼我在尋找:

Mutiple criteria in 1 query

它使用多個嵌套(?正確的說法)查詢中是否存在

Search for products with multiple criteria

一個由優素福azari的評論提及使用「查詢1」聯盟「查詢2」

要麼這些表現得更好/更合理的我想要做什麼?

獎金的問題:

我離開了上面爲簡單起見,但其實我有3個表(用於數字重視的問題,布爾值和文本) 有單獨的表的決定是因爲(據我可以想到)它可能是那個或者有一個大的答案表,有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和所有其他問題)。我們正在考慮所有常見的安全措施和可擴展性問題,但最終,我們的代碼可能會被其他人改進。

UPDATE 根據nnichols的建議,我放入了大量的數據並對不同的查詢進行了一些測試。我在桌上放置了大約250名用戶,並且在3個表中的每一箇中大約有2000個答案。我找到的鏈接提供了非常翔實的

(鏈接刪除,因爲我不能比超鏈接還兩次以上)的鏈接是在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個答案表(導致30存在)

秒10分的條件:使用 - 一個非常類似的方法(甚至是什麼呢? )這產生相同的結果

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

再次與30個子查詢。

第三個我嘗試是與上述相同(使用30聯接)

如所描述的使用的結果的前兩個EXPLAIN如下:上表中,U(相同)

主查詢有一種類型的所有(壞,雖然用戶表不是很大),搜索行大約是用戶表的大小的兩倍(不知道爲什麼)。 EXPLAIN輸出中的每一行都是相關答案表上的一個依賴查詢,其中eq_ref(good)類型使用WHERE和key = PRIMARY KEY,僅搜索1行。總體來說還不錯。

對於查詢我建議(加盟):

主要查詢實際上是你第一次參加(在我的情況AnswerBoolean)型REF(勝於一切)的任何表。搜索的行數等於任何人回答的問題數量(如50個不同的問題已被任何人回答)(這將比用戶數量少得多)。對於EXPLAIN輸出中的每個附加行,這是一個SIMPLLE查詢,其類型爲eq_ref(good),使用WHERE和key = PRIMARY KEY,僅搜索1行。總體來說幾乎相同,但起始乘數較小。

JOIN方法的最後一個優點是:它是我唯一可以知道如何通過各種值(例如n1.value)進行排序的方法。由於其他兩個查詢都使用子查詢,所以我無法訪問特定子查詢的值。添加order by子句確實改變了第一個查詢中的額外字段,並且還有'使用臨時'(我相信,對於order by),'使用filesort'(不知道如何避免這種情況)。但是,即使有這些減速,行數仍然少得多,而另外兩個(儘可能多)不能使用order by。

+0

我可能應該提到的一個細節: 在所有答案表中,主鍵是uid和qid。用戶可以回答問題或更新問題的現有答案。針對特定用戶的問題不會有多個條目。 – 2012-04-06 18:45:25

+0

您的問題表是動態的還是固定的?換句話說,它會擴大還是會在以後增加新的問題? – 2012-04-06 19:02:47

+0

隨着時間的推移,新問題將被添加或刪除。 – 2012-04-06 19:05:13

回答

0

您可以使用適當大的測試數據集和EXPLAIN和/或the profiler自己回答大部分問題。

您的INNER JOINs幾乎肯定會比切換到EXISTS更好,但這又很容易用適當的測試數據集和EXPLAIN進行測試。

+0

感謝您的輸入!我已經看過EXPLAIN,但從來沒有SHOW PROFILE。我正在努力生成一些垃圾數據進行測試,然後我將自己正確地進行測試。這種類型的搜索看起來相當普遍,所以我想知道是否有執行查詢的標準方式(我幾乎構成了上面提供的那個,儘管我確信它已經完成了)。我將測試我可能遇到的這些和其他方法的性能 - 我只是在詢問是否存在針對此類查詢的經過驗證的最佳方法。 – 2012-04-06 19:29:24

+0

我終於開始運行一些測試 - 我會把問題放在問題 – 2012-04-07 15:12:49

+0

花時間來發布您的發現做得很好。三種不同查詢的查詢時間是多少?您可能想要在值字段上嘗試索引以查看是否可以廢除文件,儘管使用這些小型數據集不應帶來太大的性能損失。我希望這被證明是一個有用的學習練習。 – nnichols 2012-04-07 17:06:12