6

这需要 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 使用索引?

**使用火鸟 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 不喜欢带大括号的部分 =/

4

1 回答 1

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对is 0 和:AREAIDis not 0使用单独的查询
  • 使用存储过程或EXECUTE BLOCK使用EXECUTE STATEMENT动态构建的语句(前一个的变体)
  • :AREAID如果为 0 ,则使存储过程 U_VIP 返回所有客户
  • 使用附加JOIN条件OR :AREAID = 0;如果 U_VIP 不为 0 返回任何结果(并且可能不执行 *),这可能不会产生结果
  • 使用 aLEFT JOIN并添加WHERE U_VIP.ID IS NOT NULL OR :AREAID = 0(可能不执行 *)
  • 使用UNION“普通”查询和第二个查询CUSTOMERwith WHERE :AREAID = 0(可能不执行 *)

对于(*),请参阅“智能逻辑”反模式

对于动态构建的查询,您可以考虑以下内容:

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

在此示例中,值为 0INPUTCONDITION将生成不带WHERE- 子句的查询,而对于其他输入,将生成带 - 子句的查询WHERE(VAR)CHAR如果参数是 a或,这样做很容易发生 SQL 注入BLOB,所以要小心。您还可以考虑两个分支,一个使用EXECUTE STATEMENT带参数,另一个不带参数。

代替 ;EXECUTE BLOCK您还可以使用您已经使用的可选过程U_VIPEXECUTE BLOCK本质上是一个不存储在数据库中的存储过程。

另请参阅“神话:动态 SQL 很慢”

于 2013-07-18T06:59:58.587 回答