2014-05-05 57 views
2

考慮一下,我有一個名爲MYBOOKS的表由超過100萬行數據組成。 我想創建一個存儲過程來返回一本特定的書,如果:BOOKID提供,或者,如果BOOKID爲0,則返回所有行。Firebird有條件的WHERE子句

因此,這裏是我想出的SQL。

SELECT * 
FROM MYBOOKS 
WHERE :BOOKID = 0 OR BOOKID = :BOOKID 

比方說,我想找到:BOOKID = 102 這工作,但它實際上返回,我正在尋找的數據只是前一行數百萬行的掃描通過。 有沒有辦法改進這個SQL?

請注意上圖已被簡化。 在實際情況下,我在WHERE子句中有更多的條件,我也想讓它們全部都是可選的。

SELECT * 
FROM MYBOOKS 
WHERE (:BOOKID = 0 OR BOOKID = :BOOKID) AND 
     (:AUTHORID = 0 OR AUTHORID = :AUTHORID) AND 
     (:TITLE = '' OR TITLE = :TITLE) 
     //..... and so on 

是否有可能改進此SQL? 謝謝。

+0

將索引添加到要搜索的字段。通常,爲每個柱子創建索引並不是最好的想法,因爲這會減慢表格中的每一項變化,但如果您不希望發生許多變化,則可以。 – pawel7318

+0

已經爲搜索字段創建索引,但它們沒有幫助。看起來放慢速度的是「:BOOKID = 0」部分,它正在對每一行進行評估。 – Kagawa

+0

確保您爲每個字段單獨創建它們,而不是一次創建它們。應該有一種方法來檢查你的查詢是否使用它們,但我不太記得FB。 – pawel7318

回答

1

使用條件WHERE是昂貴的。 Firebird不會將這些評估短路,優化器也無法以這種方式選擇最佳計劃。動態生成WHERE條件可能會更好。

其他解決方案包括使用UNION,例如,當您更換與第一個查詢:

SELECT * 
FROM MYBOOKS 
WHERE BOOKID = :BOOKID 
UNION ALL 
SELECT * 
FROM MYBOOKS 
WHERE :BOOKID = 0 

優化通常選擇此查詢一個更好的計劃,但由於要應用,這在多個條件可能是不可行的。

+0

那麼動態SQL是更好的解決方案嗎?人們通常在允許很多領域搜索/過濾時做什麼? 例如:Advance在論壇上搜索 – Kagawa

+2

UNION在這種情況下不起作用。 - 執行計劃與inital相同。 你可以嘗試這樣的事情 SELECT B.* FROM RDB$DATABASE LEFT OUTER JOIN MYBOOKS P ON (1 = 1) WHERE :BOOKID = 0 UNION ALL SELECT B.* FROM RDB$DATABASE LEFT OUTER JOIN MYBOOKS B ON (B.BOOKID = :BOOKID) WHERE :BOOKID <> 0 執行計劃是好的,但額外的過濾條件複雜的事情。 – tico

+0

這看起來很有希望,我會試試! – Kagawa