2017-08-13 26 views
0

在我們的應用程序中,我們嘗試爲給定的一組參數找到最佳匹配。我們已將這些行分成不同的質量組,這些質量組與總參數集的一部分相匹配。爲了匹配這些不同的組,我們有多個select查詢,如果沒有找到結果,我們隨後會查詢這些查詢,現在我們決定使用UNION ALL和LIMIT 1一起加入它們。來自多個查詢的第一個現有行

SET @size = 4, @price = 18, @category = 'NEW', @weight = 20, @origin = 'France'; 
(SELECT * FROM product_catalog WHERE quality = 'A1' AND size = @size AND price = @price AND category = @category AND weight = @weight AND origin = @origin LIMIT 1) 
UNION ALL 
(SELECT * FROM product_catalog WHERE quality = 'A2' AND size = @size AND price = @price AND category = @category AND origin = @origin LIMIT 1) 
UNION ALL 
(SELECT * FROM product_catalog WHERE quality = 'A3' AND price = @price AND category = @category AND weight = @weight AND origin = @origin LIMIT 1) 
UNION ALL 
(SELECT * FROM product_catalog WHERE quality = 'A4' AND price = @price AND category = @category AND origin = @origin LIMIT 1) 
UNION ALL 
... SOME MORE SELECTS ... 
LIMIT 1 

現在查詢確實按預期運行,但它執行方式比我們當前的解決方案更差。我認爲這與MySQL可能首先執行UNION語句然後意識到它只需要返回第一個語句有關。

你有什麼建議可以幫助加快查詢速度嗎?你認爲有可能將查詢重寫到一個存儲過程,該存儲過程將檢查每個查詢的結果,並在找到結果時立即返回該結果。這會加快查詢速度嗎?

+0

MySQL將評估所有部件。但有些想法:a)如果沒有總體順序,你的最後一個限制可以帶任意聯合的* ANY *未指定的隨機行,所以它不等同於嘗試第一個查詢,並且只有在沒有找到任何時才繼續下一個查詢。 b)如果爲所有組合添加索引,則此查詢應該在<0.2s內運行。不知道它是否已經這樣做了,並且您只需要它以每分鐘1000次的速度運行得更快,但除此之外,您應該首先檢查索引。c)'或','按質量排序',只有一個限制可能會比'union'更快,這取決於索引。 – Solarflare

+0

謝謝你的回答。我擔心你的觀點a),但我不確定是否屬於這種情況。此外,查詢確實運行速度在0.2秒以下,事實上甚至更快,但正如您所猜測的,我總共需要運行該查詢數百萬次。我做了一些重寫,現在我有一個解決方案,它使用一些'ORDER BY'和'(size = @size或size IS NULL)'魔法來結合查詢。 –

回答

1

首先,一些問題...

  • UNION總是建立一個tmp目錄表。 (如果可行的話,在MySQL 5.7.3和MariaDB 10.1中,這種低效率被消除了)。
  • 查詢結果缺少ORDER BY - 此可能導致得到錯誤的答案。
  • 需要第二個tmp表來完成外部ORDER BY

現在一些建議的改進。在不瞭解數據的情況下,我不得不說這些數據可能會運行得更快,也可能不會運行得更快。

避免*

而不是做SELECT *的,只是SELECT id然後JOIN回到談判桌,以獲得其餘列:

SELECT b.* 
    FROM (SELECT id ... UNION ALL ... LIMIT 1) AS a 
    JOIN product_quality AS b USING(id); 

多個索引:

INDEX(quality, size, price) 
INDEX(quality, price, category) 
... 

做一個表掃描;不需要索引。 (這需要quality值是有序的。):

SELECT * FROM ... 
    WHERE (quality = 'A1' AND size = @size AND price = @price ...) 
     OR (quality = 'A3' AND price = @price AND category = @category ...) 
    ORDER BY quality 
    LIMIT 1 

(通常情況下,我建議由UNION性能更換OR,但我認爲您的使用案例工作的其他方式。)

CASE

您的前兩個選擇可以合併:

SELECT MIN(IF(weight = @weight, 'A1', 'A2')) AS quality 
    WHERE size = @size 
     AND price = @price 
     AND category = @category 
     AND origin = @origin) 
+0

UNION ALL也進行了優化,以避免在MySQL 5.7.3中儘可能使用臨時表。 https://bugs.mysql.com/bug.php?id=50674 –

+0

感謝您提供詳盡的答案。我確實使用了特定的列而不是使用*,但爲了簡單起見,我省略了列,也許我應該提到這一點。您的OR查詢與我正在查找的內容很接近,但我認爲數據庫在返回最高質量的對象之前仍然會檢查對應情況,對嗎?如果A1質量匹配,是否有辦法立即返回而不考慮其他情況? –