2014-09-25 23 views
0

我有一個功能基礎上的名字,這是不是唯一的一個迴歸人的身份證。爭論是姓氏,名字和組織; firstname參數可以是名稱,首字母或NULL。這裏是採摘同名的一個(功能init()返回一個字符串後跟一個fulstop的第一個字母)鍵查詢:幾個ORDER BY條件,最後一個昂貴 - 如何優化它?

SELECT p.id INTO _p FROM person p 
    WHERE p.lastname = _lastname AND (_firstname IS NULL 
     OR inic(p.firstname) = _inic) 
    ORDER BY (p.firstname = _firstname) DESC, (p.organization = _org) DESC, 
     (SELECT person_score(p.id)) DESC 
    LIMIT 1; 

person_score功能是相當昂貴的 - 它搜索的人的過去活動的幾個表。數據庫未滿(測試表中只有幾千行)並且重要列上有索引,但仍然調用評分函數使得人員返回函數速度降低了七倍。如果只減慢真正需要排序的查詢,這不會成爲問題 - 爲同一組織工作的完整名稱很少見。不幸的是,EXPLAIN ANALYSE表明,即使只有一個具有給定姓氏的人,也會調用評分函數。

有什麼辦法,以確保在需要時不會破壞查詢到更多的查詢,最後得到的條件僅評估?如果不是,如何儘可能快地進行拆分(並在它們之間傳遞數據)?

一個可能的解決方案是將查詢等同於名字和組織的等級,將其存儲在數組中(而不是像現在這樣的普通整數),然後僅當數組長度大於1整數時運行評分函數。然而,這感覺很笨拙,我擔心這在常規情況下不會使功能更快(對於名字來說速度較慢)。我也只是一個非常粗略的想法如何去做,我不想在我知道這是必要的之前開始嘗試。

+0

你能把它分解成子選擇嗎?例如(選擇pid FROM person p WHERE p.lastname = _lastname AND(_firstname IS NULL OR inic(p.firstname)= _inic) ORDER BY(p.firstname = _firstname)DESC,(p.organization = _org)DESC)作爲P order by(SELECT person_score(p.id))DESC Limit 1 ----這種方式只對有限的匹配記錄集合運行功能 – Traci 2014-09-25 07:57:51

+0

最明顯的解決方法是將分數預先計算在不同的表中。您必須評估您是否需要實時分數,或者是否可能會過時,如果您可以編制更新查詢以在工作負載較低的時間或任何時候運行......或者,如果您需要實時分數,則可以修改應用程序以在需要時更新分數,或創建觸發器來執行此操作。這看起來像經典的賬戶餘額問題:從不嘗試在運行中計算它,但是預先計算它。 – JotaBe 2014-09-25 08:14:53

+0

@JotaBe:我不需要得分很近,所以你的解決方案看起來很理想。將您的評論擴展爲答案,我會接受它;或者我會明天自己實施這個解決方案並自我回答。 – 2014-09-25 08:56:37

回答

2

所以,當有不止一個結果記錄,您可以通過(p.firstname = _firstname) DESC, (p.organization = _org) DESC訂購,你仍然可以得到重複,那麼你要申請該功能爲排名第一的記錄。因此,在ORDER BY中使用帶有窗口函數的CASE結構。

SELECT p.id INTO _p 
FROM person p 
WHERE p.lastname = _lastname 
AND (_firstname IS NULL OR inic(p.firstname) = _inic) 
ORDER BY (p.firstname = _firstname) DESC, (p.organization = _org) DESC, 
    CASE WHEN COUNT(*) OVER() > 1 THEN 
    CASE WHEN COUNT(*) OVER(PARTITION BY (p.firstname = _firstname), (p.organization = _org)) > 1 THEN 
     CASE WHEN RANK() OVER(ORDER BY (p.firstname = _firstname) DESC, (p.organization = _org) DESC) = 1 THEN 
     person_score(p.id) 
     END 
    END 
    END 
LIMIT 1; 

這應該會爲您節省一些不必要的函數調用。但是,爲了確定是否必須完成一個調用,dbms必須進行一些聚合。所以這可能會更快或者不更快。你試一試。

+0

工作得很好。最初,我不確定我是否應該將問題稱爲「如何確保ORDER BY條件之一將僅在需要時進行評估」或「如何最優化我的查詢」。在第一種情況下,你的答案非常好(所以+1)。在第二種情況下,基於預先計算得分的答案會更好。這個問題不太清楚,但後者更接近我的意圖。你仍然可以擴展你的答案,提前計算,我會很樂意接受你的答案。 – 2014-09-25 09:19:09

+0

嗨帕維爾。不,我不會。我當然想過預處理,但這必須謹慎處理。對於查詢的每一次執行,預處理的數據都必須是最新的。如果person_score所需的數據每週只更改一次,請確保:預處理!但是,一旦動態更改,您需要觸發不同的表或物化視圖,這可能會很複雜。這一切都取決於排名標準改變的頻率以及需要多長時間一次。我實際上無法給你很多建議。只有:預處理時易於應用且經常需要,否則更好。 – 2014-09-25 09:29:43

+0

經過一些更多的性能檢查之後,我發現你的算法比我的具有非唯一姓氏的所有人的分數的物化視圖更快,即使我不經常刷新它,所以我將接受的答案更改爲你的答案。 – 2014-09-26 06:17:28

2

當你需要在查詢中使用一個相當耗時計算,最佳的解決方案,如果可能的話,就是有一個值在不同的表預先計算。

一種常見的情況是當你有很多條目的賬戶,你需要經常查詢餘額。通常這些系統將賬戶餘額保留在不同的表中,並保持更新。

有兩種可能性與預先計算值:

  • 他們需要的是精確的,並且是實時可用:在此情況下需要實現的是讓他們最新的解決方案。理想情況下,應用程序應該負責這樣做,但在某些情況下,你不能修改應用程序,例如,因爲它不是你的,或者數據來自幾個不同的應用程序。在這些情況下,您可以使用觸發器

  • 它們不需要準確且實時可用:您仍然可以使用以前的某些解決方案,或者可以採用更簡單的方法:準備存儲過程或查詢,用於計算所需數據並在需要時運行它(例如手動或作爲recurent作業(理想情況下在工作負載較低的時間,如果太耗時)或由某種事件進行修改。

在您的特殊情況下,您需要一張表來保留用戶的分數,並實施任何此類解決方案以根據需要進行更新。