2010-04-08 4 views
9

Compte tenu des données d'exemple suivant:SQL JOIN seulement le casse-tête de ligne maximale

Users 
+--------------------------------------------------+ 
| ID | First Name | Last Name | Network Identifier | 
+--------------------------------------------------+ 
| 1 | Billy  | O'Neal | bro4    | 
+----+------------+-----------+--------------------+ 
| 2 | John  | Skeet  | jsk1    | 
+----+------------+-----------+--------------------+ 

Hardware 
+----+-------------------+---------------+ 
| ID | Hardware Name  | Serial Number | 
+----------------------------------------+ 
| 1 | Latitude E6500 | 5555555  | 
+----+-------------------+---------------+ 
| 2 | Latitude E6200 | 2222222  | 
+----+-------------------+---------------+ 

HardwareAssignments 
+---------+-------------+-------------+ 
| User ID | Hardware ID | Assigned On | 
+-------------------------------------+ 
| 1  | 1   | April 1  | 
+---------+-------------+-------------+ 
| 1  | 2   | April 10 | 
+---------+-------------+-------------+ 
| 2  | 2   | April 1  | 
+---------+-------------+-------------+ 
| 2  | 1   | April 11 | 
+---------+-------------+-------------+ 

Je voudrais écrire une requête SQL qui donne le résultat suivant:

+--------------------+------------+-----------+----------------+---------------+-------------+ 
| Network Identifier | First Name | Last Name | Hardware Name | Serial Number | Assigned On | 
+--------------------------------------------------------------------------------------------+ 
| bro4    | Billy  | O'Neal | Latitude E6200 | 2222222  | April 10 | 
+--------------------+------------+-----------+----------------+---------------+-------------+ 
| jsk1    | John  | Skeet  | Latitude E6500 | 5555555  | April 11 | 
+--------------------+------------+-----------+----------------+---------------+-------------+ 

Mon problème est que la date maximale "Affecté sur" pour chaque utilisateur doit être sélectionnée pour chaque utilisateur individuel et utilisée pour la jointure réelle ...

Existe-t-il une manière intelligente d'accomplir cela en SQL?

+2

Vous avez donc le vieux portable de Jon Skeet? Kewl! – APC

+1

@APC: Oui! La société pensait qu'il passait trop de temps sur StackOverflow ... :) –

Répondre

9
SELECT U.NetworkIdentifier, U.FirstName, U.LastName, 
     H.HardwareName, H.SerialNumber 
    FROM (SELECT UserID, MAX(AssignedOn) LastAssignment 
      FROM HardwareAssignments 
     GROUP BY UserID) AS T 
    JOIN HardwareAssignments AS HA 
     ON HA.UserId = T.UserID AND HA.AssignedOn = T.LastAssignment 
    JOIN Users AS U ON U.ID = HA.UserID 
    JOIN Hardware AS H ON H.ID = HA.HardwareID 
ORDER BY U.NetworkIdentifier; 

La différence entre ceci et la réponse de Justin Niessner est où la sous-requête apparaît; ici, je l'ai créé dans la clause FROM. Cela garantit à peu près qu'il est exécuté une fois. Quand il y a une sous-requête corrélée dans la clause WHERE comme dans la réponse de Justin, il est possible que l'optimiseur exécute la sous-requête une fois pour chaque ligne - ce qui est plus cher quand les tables sont grandes. Un très bon optimiseur pourrait aplatir les choses de sorte que les deux soient équivalents.

+0

+1 Si l'OP est capable de faire fonctionner cette version, elle fonctionnera bien mieux que ma publication (pour les grands ensembles de données ... et tant que l'optimiseur de requête n'aplatit pas ma requête pour fonctionner de cette façon). –

+0

@Justing Niessner: Déplacé la réponse acceptée à celui-ci sur la base du commentaire ci-dessus. S'il vous plaît ne le prenez pas personnellement :) –

+0

+1 Testé brièvement, gain énorme. Cependant, il convient de noter que la requête corrélée peut mieux fonctionner si la requête externe a une sélectivité élevée et que les tables sont énormes (bien qu'il soit alors facile d'améliorer la requête ci-dessus en répétant les critères de sélection sur la requête interne). – Unreason

8
select * from Users as u 
inner join HardwareAssignments as ha 
    on u.id = ha.userid 
inner join Hardware as h 
    on uh.hardwareid = h.id 
where ha.AssignedOn = (select max(assignedon) 
         from HardwareAssignments as ha2 
         where ha.userid = ha2.userid) 

Cela pourrait vous aider à fermer. Je ne sais pas si c'est exact.

+0

+ 1 Battez-moi :) :) – Unreason

+0

Ajustez les colonnes à vos besoins, mais la partie clé de la solution est la sous-requête. –

+0

Doh! * Bill se sent un peu bête maintenant –

0

Utilisez group by et max pour filtrer les résultats de la jointure.

+0

Cela ne renvoie qu'une seule ligne. J'ai besoin d'une ligne par utilisateur. –