为什么Index不与子查询一起使用

时间:2013-07-18 05:53:39

标签: sql firebird

这需要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不喜欢使用大括号= /

的部分

1 个答案:

答案 0 :(得分:5)

而不是IN,您需要使用EXISTSINNER 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 BLOCKEXECUTE STATEMENT一起使用动态构建的语句(以前的变体)
  • 如果:AREAID为0
  • ,则使存储过程U_VIP返回所有客户
  • 使用其他JOIN条件OR :AREAID = 0;如果U_VIP没有返回0(并且可能不执行*)
  • ,则可能不会产生结果
  • 使用LEFT JOIN并添加WHERE U_VIP.ID IS NOT NULL OR :AREAID = 0(可能无法执行*)
  • 使用UNION“普通”查询和CUSTOMERWHERE :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)CHARBLOB,这样做很容易进行SQL注入,所以要小心。您还可以考虑两个分支,其中一个使用带有参数的EXECUTE STATEMENT而另一个不使用。{/ p>

您可以使用可选的过程,而不是EXECUTE BLOCK,而不是U_VIP; EXECUTE BLOCK本质上是一个未存储在数据库中的存储过程。

另见'Myth: dynamic SQL is slow'