2009-05-15 7 views
2

Je rencontre des problèmes pour trouver une requête pour ce scénario. J'ai deux tableaux que je veux utiliser dans cette requête, ils sont comme ceci:Sélection SQL aide jointe

Units 
    ID 
    Other Data 
People 
    ID 
    UnitID <-- fk to Units 
    Other Data 

Voici ce que je veux faire:
Je veux sélectionner toutes les unités qui ne disposent pas d'une ligne dans le peuple table liée à eux. Comment puis-je faire ceci?

+2

Je ne sais pas pourquoi quelqu'un a voté pour cette question. C'est une question légitime, rédigée de façon claire et concise. –

+0

@araqnid, tsql n'est pas la même chose que sql. Il y a de légères différences, et je crois que la fonction de jointure en fait partie. – Malfist

+0

mais je n'en suis pas sûr – Malfist

Répondre

8
SELECT Units.* FROM Units LEFT JOIN People ON People.UnitID = Units.ID WHERE People.ID IS NULL 

ou bien

SELECT Units.* FROM Units WHERE NOT EXISTS (SELECT 1 FROM People WHERE People.UnitID = Units.ID) 

ou même

SELECT Units.* FROM Units WHERE Units.ID NOT IN (SELECT UnitID FROM People) 
+0

lequel est le plus rapide? – Malfist

+0

Cela peut varier en fonction de la taille de vos tables, de leur taille et de la version de base de données dont vous disposez. Je vous suggère d'essayer tous les trois dans l'analyseur de requête. Pour les petits ensembles de données, il n'y aura pas de différence appréciable et, en fait, les deux premiers sont souvent reconnus par le DB comme signifiant exactement la même chose. – araqnid

+0

La table des personnes sera toujours plus grande, mais pas beaucoup. Aucun d'entre eux n'aura plus de 5 000 enregistrements. – Malfist

3
SELECT * FROM Units 
WHERE NOT EXISTS 
(SELECT * FROM People WHERE UnitID = Units.ID) 
2
SELECT * FROM Units 
WHERE ID NOT IN (SELECT UnitID FROM People) 
+0

Ceci est une mauvaise habitude. Utilisez une jointure, pas une instruction OR longue (IN se développe en OU) – ErikE

+0

Merci, je ne le savais pas. Comment "IN (SELECT ...)" se traduit-il en une série d'opérations "OU"? –

+1

Eh bien, dans cette requête simple, le moteur le convertit probablement en un JOIN. Vérifiez le plan d'exécution pour savoir à coup sûr. Mais je me concentrais sur l'aspect de la mauvaise habitude. Si ça va faire un JOIN, alors utilisez un JOIN. IN() est le meilleur pour les cas où vous savez que la liste ne sera jamais plus que quelques-uns. Si vous ne seriez pas content de le voir comme une liste d'instructions OR, alors ne l'écrivez pas de cette façon. Voici un autre aspect pour prouver que c'est une mauvaise habitude. Que faites-vous si vous voulez un autre JOIN? Commencer à imbriquer des IN? C'est un moyen rapide d'obtenir dailywtf.com – ErikE

1

Vous pouvez utiliser un pas (choisir ...)

SELECT ID 
FROM Units 
WHERE ID NOT IN (SELECT UnitID FROM People); 
+0

FWIW, il n'y a aucun avantage à utiliser DISTINCT dans cette sous-requête. –

+0

Vous avez raison, j'ai exécuté quelques plans d'exécution et il n'y a aucun avantage - même avec de grands ensembles de données. J'ai édité ma réponse et j'ai appris quelque chose;) J'avais compris que cela aurait un impact sur la rapidité d'exécution. –

1
SELECT U.* 
FROM Units U 
WHERE NOT EXISTS (
    SELECT 1 
    FROM People P 
    WHERE U.ID = P.UnitID 
) 

Veuillez noter que cela s'appelle un (anti) semi-jointure. C'est une jointure réelle et n'est pas une sous-requête corrélée.

Une autre méthode couramment utilisée est:

SELECT U.* 
FROM 
    Units U 
    LEFT JOIN People P ON U.ID = P.UnitID 
WHERE 
    P.UnitID IS NULL 

Notez que des critères supplémentaires sur la jointure (dites que vous vouliez rejoindre uniquement aux personnes qui ont été actifs) doivent être dans la clause de jointure. Cela ne marchera pas pour dire WHERE P.UnitID IS NULL AND P.Active = 1. Dans mon expérience, chacune des différentes requêtes peut s'avérer être le gagnant de la performance en fonction du plan d'exécution réel choisi. La façon dont le moteur utilise les statistiques pour prédire le nombre de lignes peut lui faire choisir des plans d'exécution sous-optimaux pour certaines requêtes, même lorsque les statistiques sont correctement mises à jour. Remarque: l'utilisation de "SELECT 1" dans vos semi-jointures au lieu de "SELECT *" permet d'économiser des cycles lors de la compilation des requêtes, puisque le * est en fait étendu à la liste des colonnes, puis supprimé ultérieurement.