2013-07-15 24 views
0

我正在構建一個動態的MySQL用戶搜索查詢,以便能夠處理許多不同的搜索條件。我考慮編寫一個存儲過程,但最終在客戶端建立了查詢(在PHP中準備好的語句)。其中的標準是能夠搜索用戶的年齡,即在X歲和Y歲之間。我想知道如何儘可能有效地做到這一點。最終查詢將相當複雜,並且有多個連接,並且可能在未來將在幾百萬行上運行,所以我需要在可能的地方優化它。我將用戶的出生日期存儲在索引爲DATE的列中,格式爲YYYY-MM-DD。我有以下的用戶定義函數(UDF)用於計算用戶的年齡:SQL查詢中的年齡計算性能

RETURN (DATE_FORMAT(current_time, '%Y') - DATE_FORMAT(date_of_birth, '%Y') - (DATE_FORMAT(current_time, '00-%m-%d') < DATE_FORMAT(date_of_birth, '00-%m-%d'))); 

計算的細節並不重要;我更關心它是如何使用的。我擔心的一個問題是,在我的WHERE子句中使用這個UDF會顯着降低查詢速度,因爲它需要在每一行上運行,即使我使UDF具有確定性。我無法保證在檢查年齡之前會有其他標準來縮小匹配的行數。我不能僅僅根據日期檢查出生日期,因爲那樣做不準確。我在考慮是否將上面的計算從UDF中拉出來,並將其直接嵌入到查詢的WHERE子句中會產生顯着的差異(我認爲是)。不利的一面是,WHERE子句通過這樣的計算進一步複雜化(或者實際上是兩個,除非有重用結果的方法)。但我想沒有辦法避免這些計算。在WHERE子句中執行這種計算是關於性能的方式,還是有更好的方法?

從理論上講,我想我甚至可以在user表中添加一個age列和計算時代,當一個用戶註冊和運行計劃工作/的cronjob每天晚上更新爲今天的生日用戶的年齡(如果我可以有效地選擇)。這肯定會加快我的搜索查詢,但會引入冗餘數據。因此,如果計算無法在搜索查詢本身內有效完成,我真的只想這樣做。

因此,總結一下:我需要搜索一系列年齡範圍內的用戶(例如25到30歲)。我應該在WHERE子句中計算年齡,還是會因爲必須在每一行都做得很慢?這是我必須作出的犧牲,還是我有更好的選擇?

任何幫助,非常感謝。

回答

4

如果您想根據當前日期的準確年齡計算,那麼你應該嘗試這樣的:

where date_of_birth between date(now()) - interval 30 years and date(now()) - interval 25 year 

在這種情況下,你是做的date_of_birth任何改造,使指數能用於查詢。

而且,你不應該使用類似的表達式:

DATE_FORMAT(current_time, '%Y') - DATE_FORMAT(date_of_birth, '%Y') 

DATE_FORMAT()將參數轉換爲字符串。你想一個數字,所以只需使用:

year(now()) - year(date_of_birth) 

它節省了從一個日期字符串爲int的轉換而直接進入到一個int。

編輯:

要處理的「25」的真正意思是「高達26」的情況下,實現邏輯有明確的比較:

where date_of_birth >= date(now()) - interval 30 years and 
     date_of_birth < date(now()) - interval 26 year 
+0

謝謝你的答案。例如,對於這個年齡段,25歲半的人仍應該與查詢20-25歲的人相匹配。所以我不會從今天的日期減去25年來尋找,但希望能夠匹配那些25歲但還未26歲的人。我希望這是有道理的,如果我不清楚這一點,我很抱歉。你知道如何做到這一點?這也是爲什麼我沒有采用間隔方法。我使用的功能只是我在某處找到的一個功能,知道轉換不是很好的做法。我會像你說的那樣更新它。 – Andy0708

+1

計算他們的DOB必須達到的標準而不是計算他們當前的年齡肯定是要走的路。 –

2

這不是真正關心的UDF或存儲的性能程序。每當你在列上使用函數時,MySQL都不能使用它的索引。

如果您不希望Highlander在您的數據庫中,那麼年齡的tinyint無符號列就足夠了(0-255)。這花費1個字節/行。你可以在其上放置一個索引。這個列添加到表中的開銷是微不足道的。不要害怕存儲空間。另一方面,存儲性能更受關注。全掃描搜索的成本遠高於1字節的額外列。

您可以使用date_of_birth列上的觸發器更新此列。當然,如果你在桌子上放置適當的索引,夜間cronjob可以有效地選擇date_of_birth = DATE(NOW())的行並將年齡增加一。 (我會用存儲過程來做到這一點,所以一切都可以在MySQL中完成)。

ps .:你寫的函數似乎是一個存儲函數,而不是一個UDF。存儲的函數用SQL編寫並存儲在MySQL中。 UDF是用C編寫的.so或.dll文件加載到MySQL中的。欲瞭解更多信息,你可以檢查:Help with SP and UDF?

+0

謝謝。我完全同意你的看法。然而,我擔心的不是附加字節,而是更多的是爲了避免「維護」年齡。雖然它會相對簡單,但是cronjobs,觸發器等都會使我的應用程序整體變得更復雜一些。如果有必要的話,這不是一件壞事(它的確會給我很好的表現),但是如果在搜索查詢中不能有效地進行比較,我會更樂意去找這樣的解決方案。否則,爲了簡單起見,我寧願處理所有事情。 :-) – Andy0708