包含IN语句的OUTER JOIN的索引用法

时间:2009-12-10 16:04:29

标签: sql indexing outer-join

SELECT a.*, b.* 
FROM a
    LEFT OUTER JOIN b 
        ON b.user IN (:userlist) 
        AND b.key = a.fk_to_b
WHERE 
a.user IN (:userlist) 
OR b.user IN (:userlist)
  • 表b的索引为:(用户,密钥)

当:userlist参数包含单个值时,数据库仅使用索引。当:用户包含多个值(内部扩展为多个OR语句?)时,不使用索引并执行表扫描(b)。

为什么数据库在提供多个:userlist值时不使用索引?

有谁知道这个查询的更优化版本?

3 个答案:

答案 0 :(得分:1)

此查询适用于所有主要系统,可能效率更高:

SELECT  a.*, NULL
FROM    a
WHERE   a.user IN (:userlist)
        AND a.fk_to_b NOT IN
        (
        SELECT  key
        FROM    b
        )
UNION ALL
SELECT  a.*, b.id
FROM    a
JOIN    b
ON      b.key = a.fk_to_b
WHERE   b.user IN (:userlist)

您能告诉我您使用哪个RDBMS吗?

答案 1 :(得分:0)

快速回答是:这取决于。

如果在:userlist中指定多个值,则数据库服务器可以选择以不同的方式优化查询,例如,它可以选择全表扫描。

大多数情况下,最好的选择是通过执行

来查看查询的优化方式
  1. Oracle中的EXPLAIN PLAN
  2. 在SQL server中显示执行计划。
  3. 为了帮助您,我们确实需要知道您正在使用哪个数据库。

答案 2 :(得分:0)

IN(:userlist)扩展为多个OR语句 查询优化器忽略OR行/子句 如果数据库是Oracle,那么该怎么做:

CREATE TABLE userListTable  
(  
   sessionId NUMBER(9),  
   user      NUMBER(9)  
);  

CREATE INDEX userListTableMulti1 ON userListTable(sessionId,user);  

...

CREATE OR REPLACE FUNCTION fn_getUserList(parmUserList VARCHAR2)  
   RETURN NUMBER DETERMINISTIC  
   varUser      NUMBER(9);  
   varSessionId NUMBER(9);  
BEGIN  
   varSessionId := sys_context('USERENV','SESSIONID');  

   -- You have to work on a VARCHAR2TOLIST() function  
   FOR varUser IN VARCHAR2TOLIST(parmUserList) LOOP  
      INSERT INTO userListTable(sessionId,user)  
      VALUES(varSessionId, varUser)  
   END LOOP;  

   INSERT INTO resultsTable  
      SELECT  
         varSessionId as sessionId ,  
         a.*                       ,  
         b.*  
      FROM  
         (SELECT a.*  
          FROM a  
             INNER JOIN userListTable  
             ON a.user = userListTable.user AND  
                userListTable.sessionId = varSessionId) a  
         LEFT OUTER JOIN (SELECT b.*  
                          FROM b  
                             INNER JOIN userListTable  
                             ON b.user = userListTable.user AND  
                                userListTable.sessionId = varSessionId) b  
         ON b.key = a.fk_to_b;  

   RETURN varSessionId;  
END;  
/  

...

// C Client side  
int   varSessionId;  
char* parmUserList;  
char* sqlStr;  

...  

sqlStr = (char*)malloc( strlen(parmUserList) + 17 ) ;  
sprintf(sqlStr,"fn_getUserList(%s)", parmUserList);  

// EXEC_SQL_FUNC_C_MACRO  
// EXEC_SQL_RETURN_QUERY_RESULTS_C_MACRO  
// EXEC_SQL_C_MACRO  
// are all based on the database API C libraries  

// Run the function for this session  
varSessionId = EXEC_SQL_FUNC_C_MACRO(sqlStr);  
free(sqlStr);  

// Get the results  
sqlStr = (char*)malloc(128);  
sprintf(  
   sqlStr,  
   "SELECT * "  
   "FROM resultsTable "  
   "WHERE sessionId=%s",  
   varSessionId);  
EXEC_SQL_RETURN_QUERY_RESULTS_C_MACRO(sqlStr);  
free(sqlStr);  

...  

// Clean up the resultsTable for this session  
sqlStr = (char*)malloc(128);  
sprintf(  
   sqlStr,  
   "DELETE "  
   "FROM resultsTable "  
   "WHERE sessionId=%s",  
   varSessionId);  
EXEC_SQL_C_MACRO(sqlStr);  
free(sqlStr);  

// Clean up the userListTable for this session  
sqlStr = (char*)malloc(128);  
sprintf(  
   sqlStr,  
   "DELETE "  
   "FROM userListTable "  
   "WHERE sessionId=%s",  
   varSessionId);  
EXEC_SQL_C_MACRO(sqlStr);  
free(sqlStr);