这需要0.001秒才能执行,它使用索引搜索
SELECT * FROM CUSTOMER WHERE ID IN (1008,1122)
现在我有一个存储过程U_VIP,返回与示例一相同的ID(1008,1122),执行只需0.001秒
SELECT ID FROM U_VIP //returns (1008,1122)
现在,当我将它们组合起来时,执行并且未使用索引需要大约半秒
SELECT * FROM CUSTOMER WHERE ID IN (SELECT ID FROM U_VIP)
我简化了上面的例子,在实际应用中,性能受到更高幅度的影响。在这种情况下如何强制Firebird使用索引?
**使用Firebird 2.1
**编辑**
根据Mark的回答,使用JOIN会改善执行时间,因为它现在正在进行索引搜索。
SELECT CUSTOMER.*
FROM CUSTOMER
INNER JOIN U_VIP ON U_VIP.ID = CUSTOMER.ID
这很好,但是,它为我引入了另一个问题,我将在下面的例子中尝试解释。
SELECT CUSTOMER.*
FROM CUSTOMER
WHERE (:AREAID = 0 OR ID IN (SELECT ID FROM U_VIP(:AREAID)))
使用where子句,我可以根据是否有条件地应用过滤器:AREAID是否由用户提供。当我用连接替换where子句时,我如何实现相同的目的?
类似的东西:
SELECT CUSTOMER.*
FROM CUSTOMER
{IF :AREAID > 0 THEN}
INNER JOIN (SELECT ID FROM U_VIP(:AREAID)) VIP ON VIP.ID = CUSTOMER.ID
{END IF}
当然,Firebird不喜欢使用大括号= /
的部分答案 0 :(得分:5)
而不是IN
,您需要使用EXISTS
或INNER JOIN
。我不完全确定细节,但我相信你的查询完全阅读CUSTOMER
表,评估每一行子查询的结果(甚至可能为每一行执行子查询)。由于优化器事先不知道子查询的结果数,因此如果您使用固定数量的文字值(例如在第一个查询中),它就无法创建优化。
尝试将您的查询更改为:
SELECT *
FROM CUSTOMER
WHERE EXISTS (SELECT 1 FROM U_VIP WHERE U_VIP.ID = CUSTOMER.ID)
或者:
SELECT CUSTOMER.*
FROM CUSTOMER
INNER JOIN U_VIP ON U_VIP.ID = CUSTOMER.ID
或(有时改变订单可以带来更好的表现):
SELECT CUSTOMER.*
FROM U_VIP
INNER JOIN CUSTOMER ON CUSTOMER.ID = U_VIP.ID
一般情况下,我希望这些查询的效果优于使用IN
的查询。
编辑以响应更新
根据您更新的问题,我可以想到多种解决方案,但我对它们的表现并不完全确定。
:AREAID
使用单独的查询为0且:AREAID
不为0 EXECUTE BLOCK
与EXECUTE STATEMENT
一起使用动态构建的语句(以前的变体):AREAID
为0 JOIN
条件OR :AREAID = 0
;如果U_VIP没有返回0(并且可能不执行*)LEFT JOIN
并添加WHERE U_VIP.ID IS NOT NULL OR :AREAID = 0
(可能无法执行*)UNION
“普通”查询和CUSTOMER
上WHERE :AREAID = 0
的第二个查询(可能不会执行*)对于(*),请参阅'Smart logic'反模式
对于动态构建的查询,您可以考虑以下内容:
EXECUTE BLOCK (INPUTCONDITION INTEGER = ?)
RETURNS (ID INTEGER)
AS
DECLARE VARIABLE QUERY VARCHAR(6400);
BEGIN
QUERY = 'SELECT a.ID FROM SORT_TEST a';
IF (INPUTCONDITION <> 0) then
QUERY = QUERY || ' WHERE a.ID = ' || INPUTCONDITION;
FOR EXECUTE STATEMENT QUERY INTO :ID
DO
SUSPEND;
END
在此示例中,INPUTCONDITION
的值0将生成不带WHERE
- 子句的查询,而对于其他输入则生成带有WHERE
- 子句的查询。如果参数是(VAR)CHAR
或BLOB
,这样做很容易进行SQL注入,所以要小心。您还可以考虑两个分支,其中一个使用带有参数的EXECUTE STATEMENT
而另一个不使用。{/ p>
您可以使用可选的过程,而不是EXECUTE BLOCK
,而不是U_VIP
; EXECUTE BLOCK
本质上是一个未存储在数据库中的存储过程。