2015-02-23 65 views
1

我有一個表line_item { id: int, price: decimal, quantity: int, [other:...] }。這張桌子非常大,約。 2800萬行。現在我想獲得排前1000行order by f(price, quantity, [other...]),f是一個任意函數。什麼是最好的方式來做到這一點?通過MySQL中的複雜表達式獲取N行的有效方法

我想2個解決方案:

  1. 使用order bylimit。這種方式可能會很慢,因爲我認爲MySQL爲每行計算結果f然後對它們進行排序。
  2. 創建新列以存儲函數f的結果。這種方式對擴展性不好,因爲也許我想在不同的上下文中使用多個功能ff1,f2 ...)。

我真的希望有第三種解決方案比他們更好。

回答

0

(對不起,這是一個否定的答案,但這就是生活。)

如果你將接受「最佳解決方案」暫時只快兩倍,你經歷了什麼,然後接受@ Zsuzsa的。

我在這裏告訴你,如果不對f(...)做些什麼,它就無法被優化。原因如下:

優化器不會看到WHERE子句,但會看到帶有表達式的ORDER BY。因此,它意識到評估查詢的唯一方法是執行「表掃描」(即,讀取所有行),評估每行的函數,將結果保存在tmp表(具有28M行),排序該tmp表,並提供1000行。

可以該函數的任何被複制到WHERE子句中用於過濾出一些行嗎?如果是這樣,tmp表可能會更小。或者,如果你很幸運,也許可以設計一些INDEX,以便它不必進行全表掃描。

你在修改所有行嗎?或者是這種「只寫」表?也就是說,一旦寫成一行,永不改變?在此基礎上,可以對所有'舊'行預先計算f()嗎?如果是這樣,請將它存儲在某處並添加一個索引 - Poof!即時結果。

f()是測試某個日期範圍的常見部分嗎? (大表格通常有某種日期,大表格上的查詢通常會詢問「最近」的項目。)如果是這樣,那麼可以從f()中取出。然後我們可以考慮按日期對錶進行分區。這樣,即使f中沒有其他東西可以優化,「分區修剪」可能會限制要處理的行數。

請SHOW CREATE TABLE並討論一下這裏的一些想法是否可行。

+0

是的,f()取決於日期值。因此存儲預先計算的值不是一個好方法。在像stackoverflow這樣的大系統中,他們只是在用戶請求時計算這個值,而他們並不關心大問題表。 – Adam 2015-02-27 07:52:03

+0

一個像stackoverflow這樣的大系統設計它的數據庫和它的用戶期望值,所以當用戶在等待時,它不必掃描28M行。 – 2015-02-27 17:27:01

+0

stackoverflow如何做到這一點?我看到了stackoverflow有這樣的排序:排序熱點問題,積極的問題......我認爲他們比我的問題更復雜。你能給我一些建議嗎? – Adam 2015-02-27 19:14:32

0

我正在考慮另一種選擇:

只用ID和f列創建臨時表。

創建第二個臨時表(temp_table2),並在其中插入以下結果:

SELECT TOP 1000 id, f 
FROM temp_table 
ORDER BY f 

這應該跑得比你提到的其他2個選項速度更快,因爲在這裏你必須只用2列工作。

最後,您可以通過將此seoncd臨時表連接到原始表來選擇最終結果。

SELECT line_item.* --or just the columns you need 
FROM temp_table2 
INNER JOIN line_item 
ON temp_table2.id = line_item.id 

您還可以嘗試執行您提到的第一個選項,並查看使用我建議的臨時表是否有顯着的性能改進。 臨時表的使用可以在很多情況下提高執行時間,但不是所有的時間 - 所以最好的方法是嘗試兩種方法,看看哪個更好。

相關問題