2014-05-05 56 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