2010-02-11 7 views
2

J'ai des contacts qui peuvent appartenir à plusieurs groupes et avoir plusieurs requêtes. Je dois simplement obtenir des contacts pour un groupe spécifique qui n'a pas de demandes spécifiques.Sql Server query perfomance

Comment améliorer les performances de cette requête:

SELECT top 1 con_name , 
     con_id 
FROM tbl_group_to_contact gc 
     INNER JOIN tbl_contact c ON gc.con_id = c.id 
WHERE group_id = '81' 
     AND NOT c.id IN (SELECT con_id 
          FROM  tbl_request_to_contact 
          WHERE  request_id = '124') 

Quand je lance cette requête avec plan Explication il montre que cette requête:

SELECT con_id 
          FROM  tbl_request_to_contact 
          WHERE  request_id = '124' 

est cher à l'utilisation d'un index cherchent.

|--Top(TOP EXPRESSION:((1))) 
     |--Nested Loops(Left Anti Semi Join, OUTER REFERENCES:([c].[id])) 
      |--Nested Loops(Inner Join, OUTER REFERENCES:([gc].[con_id], [Expr1006]) WITH UNORDERED PREFETCH) 
      | |--Clustered Index Scan(OBJECT:([db_newsletter].[dbo].[tbl_group_to_contact].[PK_tbl_group_to_contact_1] AS [gc]), WHERE:([db_newsletter].[dbo].[tbl_group_to_contact].[group_id] as [gc].[group_id]=(81)) ORDERED FORWARD) 
      | |--Clustered Index Seek(OBJECT:([db_newsletter].[dbo].[tbl_contact].[PK_tbl_contact] AS [c]), SEEK:([c].[id]=[db_newsletter].[dbo].[tbl_group_to_contact].[con_id] as [gc].[con_id]) ORDERED FORWARD) 
      |--Top(TOP EXPRESSION:((1))) 
       |--Clustered Index Seek(OBJECT:([db_newsletter].[dbo].[tbl_request_to_contact].[PK_tbl_request_to_contact] AS [cc]), SEEK:([cc].[request_id]=(124)), WHERE:([db_newsletter].[dbo].[tbl_contact].[id] as [c].[id]=[db_newsletter].[dbo].[tbl_request_to_contact].[con_id] as [cc].[con_id]) ORDERED FORWARD) 
+0

peut pas le faire hors le brassard, mais vous vous trouverez peut-être pouvez supprimer le sous-sélection et utiliser une jointure externe sur 'tbl_request_to_contact' combinée avec une clause' GROUP BY' et le qualificatif 'HAVING' de (par exemple, 'HAVING COUNT (request_id) = 0') peut aider. –

+1

Dans 'SQL Server',' LEFT JOIN' est pire que 'IN', sans parler de' GROUP BY/HAVING' (qui peut facilement être remplacé par 'IS NULL' dans la clause 'WHERE'). – Quassnoi

+0

À quoi sert le request_id = '124'? Les autres identifiants de requête n'ont pas d'importance? – Brettski

Répondre

2

Votre requête est ok, il suffit de créer les indices suivants:

tbl_request_to_contact (request_id, con_id) 
tbl_group_to_contact (group_id, con_id) 

Puisque les tableaux semblent être les tables de lien, vous voulez faire de ces composites les clés primaires:

ALTER TABLE tbl_request_to_contact ADD CONSTRAINT pk_rc PRIMARY KEY (request_id, con_id) 
ALTER TABLE tbl_group_to_contact ADD CONSTRAINT pk_gc (group_id, con_id) 

, en s'assurant que request_id et group_id passent en premier.

De plus, si votre request_id et group_id sont des nombres entiers, passer les entiers comme les paramètres, et non des chaînes:

SELECT con_name, con_id 
FROM tbl_group_to_contact gc 
JOIN tbl_contact c 
ON  c.id = gc.con_id 
WHERE group_id = 81 
     AND c.id NOT IN 
     (
     SELECT con_id 
     FROM tbl_request_to_contact 
     WHERE request_id = 124 
     ) 

, ou une conversion implicite peut se produire rendre les index inutilisables.

Mise à jour:

de votre plan, je vois que vous manquez l'index sur tbl_group_to_contact. Une analyse de table complète est requise pour filtrer les groupes.

Créer l'index:

CREATE UNIQUE INDEX ux_gc ON tbl_group_to_contact (group_id, con_id) 
+0

Acceptez le premier index. Voulez-vous expliquer sur la seconde? Souhaitez ajouter INCLUDE (con_name) –

+0

vous savez quoi? J'ai supprimé le top 1 de l'instruction select et il a commencé à travailler beaucoup plus vite, comment pouvez-vous l'expliquer? mon j'utilise une autre approche pour sélectionner une seule ligne? – Sasha

+0

@Frank: le second aide juste à filtrer les groupes. 'con_name' appartient très probablement à' tbl_contact', pas 'tbl_group_to_contact'. – Quassnoi

0

Je suis d'accord avec @Quassnoi avec les index. De plus, vous pouvez utiliser une jointure à gauche pour afficher uniquement les utilisateurs qui n'ont pas de demandes. Cela a généralement de meilleures performances qu'une sous-requête.

À quoi sert le request_id = '124'? Les autres identifiants de requête n'ont pas d'importance?

SELECT con_name , 
     con_id 
FROM tbl_group_to_contact gc 
     INNER JOIN tbl_contact c ON gc.con_id = c.id 
     LEFT JOIN tbl_request_to_contact rtc ON gc.con_id = rtc.con_id 
WHERE group_id = '81' and rtc.request_id IS NULL 
+0

http://explainextended.com/2009/09/15/not-in-vs-not-exists-vs-left-join-is-null-sql-server/ – Quassnoi

+0

Je ne peux pas utiliser request_id IS NULL, J'ai besoin de request_id spécifique – Sasha

+0

@Quassnoi: concernant votre article ... est-il possible de vous contacter pour une question ou deux? –