2012-08-03 85 views
2

我有這個疑問:SQL的性能問題

SELECT stringa FROM table WHERE stringb = 'x' OR stringb = 'y' OR stringb = 'z' 

這只是一個縮短的版本,實際的查詢擁有1000「OR」子句在一個查詢。

它需要幾分鐘時間執行,這是不好的。

我試過在時間做一個查詢,像這樣:

SELECT stringa FROM table WHERE stringb = 'x' 
SELECT stringa FROM table WHERE stringb = 'y' 
SELECT stringa FROM table WHERE stringb = 'z' 

但是,這需要更長的時間。我也試過這樣一個大的查詢:

SELECT stringa FROM table WHERE stringb = 'x' 
UNION 
SELECT stringa FROM table WHERE stringb = 'y' 
UNION 
SELECT stringa FROM table WHERE stringb = 'z' 

但是再次花費了更長的時間。

如果有人有任何提高性能的建議,將不勝感激。我的表是MyISAM,如果它很重要。

編輯:

下面是表的結構:

列:

key (CHAR PRIMARY), stringa (CHAR), stringb (CHAR) 

,行看起來像這樣:(鍵 - stringa - stringb)

key - a - b 
key - a - c 
key - a - d 
key - a - e 
key - a - f 
key - b - b 
key - b - c 
key - b - d 
key - c - c 
key - c - d 
key - c - f 
key - d - f 

等..有近百萬行。

,我需要選擇所有「stringa」,其中「stringb」等於A或B或C等

當然stringa和stringb不只是「a」和「B」,它們包含的字符長度在3到80個字符之間變化。

我希望以某種方式

+0

如果單列的查詢花了很長一段時間,你很可能缺少一個索引。 – dasblinkenlight 2012-08-03 21:26:37

+0

通常,「選擇」使任何數據庫系統的優化器難以使用任何類型的索引,並且性能受到嚴重影響。儘量只選擇你需要的列值。如果沒有一列,請在需要的列或列上放置一個索引。 – Mithrandir 2012-08-03 21:27:08

+1

你從哪裏得到你的OR子句比較?你不能做一個'SELECT列WHERE一個in(SELECT valid_terms FROM other_table)'嗎?或者是每個OR真的在具有單個值的不同列上?如果是後者...有些東西不對 – 2012-08-03 21:27:54

回答

2

首先幫助,列數據類型更改爲varchar。儘管你可能已經聽說char被認爲速度更快,但爲了I/O的巨大增加(非常糟糕的交易),權衡是爲了節省CPU的一小部分。

其次,如果還沒有列,您需要列stringb上的索引。索引不一定是唯一的。

第三,很多DBMS對成千上萬的OR條件沒有問題,儘管通常這樣的事情被表示爲WHERE stringb IN ('a', 'b', 'c', 'd', 'e' ...)

最後,在很多情況下,JOIN如果不能提供優異的性能(儘管在某些DBMS或情況下可能),至少可以提供更高的清晰度和重用性。例如,許多人做的一件事就是創建一個字符串拆分函數,當以'a,b,c,d,e'的格式傳遞一個字符串時,會返回一個包含每個項目的行集。加入此行集非常簡單,只要客戶端可以構建要拆分的字符串,就可以使查詢能夠被動態驅動。

這裏是一個可能的方式做一個JOIN:

​​
1

您需要創建在stringb列的索引。

你的問題更多的是你正在做一個全表掃描,而不是「或」的效率。在「in」語句中傳統值列表是傳統的。然而,在一些數據庫中,這對性能沒有影響。

此外,你的領域是在char或varchar聲明?如果它們是字符,那麼這可能是性能問題的根源。這些將被填充空間,大大增加了存儲空間並延長了比較時間。

+0

感謝您的回答。對不起,我是一個新手,所以請裸露在我身邊,但不索引的必須是獨特的?我的字符串值不是唯一的。所有3列都被聲明爲CHAR(90),我在某處讀取CHAR,因爲填充的空間會創建更大的表,但速度更快,因爲SQL不必確定數據結束的位置,例如VARCHAR。我在那裏誤導了嗎? – amba88 2012-08-03 22:10:21

+0

索引不一定是唯一的!另外,將您的列定義更改爲varchar。您爲每個字符串存儲90多個字符,即使是空字符也是如此。這個改變可能會對你的查詢產生很大的影響,因爲你會將磁盤上的數據大小減小一個數量級。 – 2012-08-03 22:23:00

0

嘗試

SELECT stringa FROM table WHERE stringb = 'x' 
UNION ALL 
SELECT stringa FROM table WHERE stringb = 'y' 
UNION ALL 
SELECT stringa FROM table WHERE stringb = 'z' 

或@ ErikE的解決方案,如果你真的有一千或條件。

由於您的選擇是互斥的,因此UNION ALL應該比UNON快得多,您不需要使查詢刪除複製聯合的方式。

+0

UNION和UNION ALL都是在每個查詢上都進行序列化的,所以它沒有優於串行運行查詢的優勢,並且由於它將所有行分段到一個臨時表中,因此速度變慢。 – 2012-08-04 01:47:07

0

儘管我認爲@HLGEM第二回答最好,您也可以嘗試在查詢中使用正則表達式來處理字符串b。

+0

你有什麼機會展示如何做到這一點? – ErikE 2012-08-04 01:02:38

+0

假設有問題的數據庫支持正則表達式,OR的正則表達式可以創建爲像stringb = a | b | c | ... 這只是一個選項。我真的不知道這是否能解決問題,但我會盡力去做。當然,爲你的數據庫創建正則表達式不在這個問題的範圍之內。 – fabiopagoti 2012-08-04 01:48:16

+0

它會做一個全表掃描,所以它不是很有用。在MySQL中,RLIKE函數將被使用。 where stringb RLIKE'a | b | c' – 2012-08-04 01:52:32

1

首先,正如其他人所建議的,VARCHAR是比CHAR更好的選擇。 CHAR不會更快。

考慮用KEY(stringb)PARTITIONS 8(這是任意的)對錶進行分區並在(stringb,stringa)上添加一個索引。這將減少IO,並且覆蓋索引將使返回數據更快。

在並行中運行相等查找。運行:

SELECT stringa FROM table WHERE stringb in('x',...) 
SELECT stringa FROM table WHERE stringb in('y',...) 
SELECT stringa FROM table WHERE stringb in('z',...) 

在三個線程中會導致顯着的性能提升。

你只需要把結果放回到一起,這並不困難。碎片查詢可用於自動並行與查詢()名單,如果你想看看它:

http://code.google.com/p/shard-query