2009-12-10 4 views
2
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) 
  • Tableau B présente un indice de: (utilisateur, clé)

La base de données utilise seulement l'index lorsque l': paramètre userlist contient une valeur unique . Quand: users contient plusieurs valeurs (qui s'étendent en interne à plusieurs instructions OR?), L'index n'est pas utilisé et un balayage de table (de b) est effectué.utilisation des index avec OUTER JOIN qui contient dans l'état

Pourquoi la base de données n'utilise-t-elle pas l'index lorsque plusieurs valeurs: liste d'utilisateurs sont fournies?

Est-ce que quelqu'un connaît une version plus optimale de cette requête?

+0

qui 'RDBMS' utilisez-vous? – Quassnoi

+0

DB multiples - principalement Sybase, Ingres et Firebird. – Starfield

Répondre

1

Cette requête fonctionne dans tous les principaux systèmes et sera probablement plus efficace:

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) 

Pourriez-vous s'il vous plaît dire quel RDBMS utilisez-vous?

+0

DB multiples - principalement Sybase, Ingres et Firebird. Réponse intéressante! Malheureusement, nous utilisons des clés primaires composées pour que nous puissions seulement implémenter le NOT IN en utilisant la concaténation de chaîne (egax + ay NOT IN (SELECT x + y FROM b). plus lent que ma requête actuelle sur Ingres – Starfield

0

La réponse rapide est: Cela dépend.

Si vous spécifiez plusieurs valeurs dans: liste_utilisateurs, le serveur de base de données peut choisir d'optimiser la requête d'une manière différente, il peut par exemple choisir une analyse de table complète.

La plupart du temps, la meilleure option est de voir comment la requête est optimisée, en faisant

  1. EXPLAIN PLAN Oracle
  2. Afficher le plan d'exécution dans le serveur SQL.

Pour vous aider, nous avons vraiment besoin de savoir quelle base de données vous utilisez.

0

IN (: userlist) se développe en plusieurs instructions OR.
L'optimiseur de requête ne tient pas compte des lignes/clauses OR.
est ici ce qu'il faut faire si le DB est 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); 
+0

Vous devrez également effectuer des balayages périodiques des tables resultsTable et userListTable pour nettoyer les données laissées par les sessions d'applications côté client interrompues, ce qui signifie que l'utilisation d'un ID session pour garder la trace des transactions vous libère. utilisez complètement la base de données sans avoir à vous pencher en arrière pour extraire des données à l'aide de SQL compliqué, et vous constaterez que cela facilite le maintien à long terme. –