English 中文(简体)
Index usage with OUTER JOIN that contains IN statement
原标题:
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)
  • Table b has an index of: (user, key)

The database only uses the index when the :userlist parameter contains a single value. When :users contains multiple values (which internally expands to multiple OR statements?) the index is not used and a table scan (of b) is performed.

Why isn t the database using the index when multiple :userlist values are supplied?

Does anyone know of a more optimal version of this query?

最佳回答

This query will work in all major systems and probably will be more efficient:

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)

Could you please tell which RDBMS do you use?

问题回答

The quick answer is: It depends.

If you specify multiple values in :userlist, then the database server may choose to optimise the query in a different way, it may choose a full table scan for instance.

Most of the time, the best option is to see how the query is optimised, by doing

  1. EXPLAIN PLAN in Oracle
  2. Show Execution Plan in SQL server.

To help you more, we really need to know which database you re using.

IN (:userlist) expands to multiple OR statements.
The query optimizer disregards OR lines/clauses.
Here s what to do if the DB is 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);  




相关问题
SQL SubQuery getting particular column

I noticed that there were some threads with similar questions, and I did look through them but did not really get a convincing answer. Here s my question: The subquery below returns a Table with 3 ...

难以执行 REGEXP_SUBSTR

I m 查询Oracle 10g。 我有两张表格(样本数据见下文)。 i m 试图提取一些领域

SQL Query Shortcuts

What are some cool SQL shorthands that you know of? For example, something I learned today is you can specify to group by an index: SELECT col1, col2 FROM table GROUP BY 2 This will group by col2

PHP array callback functions for cleaning output

I have an array of output from a database. I am wondering what the cleanest way to filter the values is example array Array ( [0] => Array ( [title] => title 1 ...

OracleParameter and DBNull.Value

we have a table in an Oracle Database which contains a column with the type Char(3 Byte). Now we use a parameterized sql to select some rows with a DBNull.Value and it doesn t work: OracleCommand ...

Running numbers in SQL

I have a SQL-statement like this: SELECT name FROM users WHERE deleted = 0; How can i create a result set with a running number in the first row? So the result would look like this: 1 Name_1 2 ...

How to get SQL queries for each user where env is production

I’m developing an application dedicated to generate statistical reports, I would like that user after saving their stat report they save sql queries too. To do that I wrote the following module: ...

热门标签