我的團隊在一個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等來訂購。
第一個問題: 這個表組織是否至少有點體面?我們認爲,由於問題的數量是可變的,並不是每個用戶都回答每個問題,所以這樣的問題是必要的。
主要問題: 查詢方式效率太低了嗎?我認爲將自己的桌子加入自己可能需要一兩次(如果我們最終提出這麼多問題的話)是不理想的。我做了一些搜索,發現這兩個職位,這似乎那種淡淡的什麼我在尋找:
它使用多個嵌套(?正確的說法)查詢中是否存在
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。
我可能應該提到的一個細節: 在所有答案表中,主鍵是uid和qid。用戶可以回答問題或更新問題的現有答案。針對特定用戶的問題不會有多個條目。 – 2012-04-06 18:45:25
您的問題表是動態的還是固定的?換句話說,它會擴大還是會在以後增加新的問題? – 2012-04-06 19:02:47
隨着時間的推移,新問題將被添加或刪除。 – 2012-04-06 19:05:13