2009-01-30 12 views
49

J'ai des données qui ressemble à ceci:Comment joindre la ligne la plus récente d'une table à une autre table?

entities 
id   name 
1   Apple 
2   Orange 
3   Banana 

périodiquement, un processus se déroulera et donner un score à chaque entité. Le processus génère les données et l'ajoute à une marque table comme si:

scores 
id entity_id score date_added 
1 1   10  1/2/09 
2 2   10  1/2/09 
3 1   15  1/3/09 
4 2   10  1/03/09 
5 1   15  1/4/09 
6 2   15  1/4/09 
7 3   22  1/4/09 

Je veux être en mesure de sélectionner toutes les entités ainsi que les plus récentes score enregistré pour chaque résultat des données comme celle-ci:

entities 
id name  score date_added 
1 Apple  15  1/4/09 
2 Orange 15  1/4/09 
3 Banana 15  1/4/09 

Je peux obtenir les données pour une seule entité utilisant cette requête:

SELECT entities.*, 
     scores.score, 
     scores.date_added 
FROM entities 

INNER JOIN scores 
ON entities.id = scores.entity_id 

WHERE entities.id = ? 

ORDER BY scores.date_added DESC 
LIMIT 1 

Mais je suis à une perte pour savoir comment sélectionner les mêmes pour toutes les entités. Peut-être que ça me regarde en face?

Merci beaucoup d'avoir pris le temps.

Merci pour les bonnes réponses. Je vais lui donner quelques jours pour voir si une solution préférée éclabousse alors je vais sélectionner la réponse. MISE À JOUR: J'ai essayé plusieurs des solutions proposées, le principal problème auquel je suis confronté est que si une entité n'a pas encore de score généré, elle n'apparaît pas dans la liste. À quoi ressemblerait le code SQL pour s'assurer que toutes les entités sont renvoyées, même si elles n'ont encore aucun résultat?

MISE À JOUR: Réponse sélectionnée. Merci tout le monde!

Répondre

60

je le fais de cette façon:

SELECT e.*, s1.score, s1.date_added 
FROM entities e 
    INNER JOIN scores s1 
    ON (e.id = s1.entity_id) 
    LEFT OUTER JOIN scores s2 
    ON (e.id = s2.entity_id AND s1.id < s2.id) 
WHERE s2.id IS NULL; 
+1

Merci Bill, j'ai fini par me contenter de cette solution, mais j'ai remplacé l'INNER JOIN par un LEFT JOIN pour inclure les entités qui n'ont pas encore de scores. – GloryFish

+1

J'aime cette solution, j'utilise aussi LEFT JOIN. Comment recommanderiez-vous de traiter une égalité, dans le cas où il y avait deux scores pour la même entité avec la même date? – russds

+0

@russds, utilisez une autre colonne pour résoudre les liens. –

1
SELECT entities.*, 
     scores.score, 
     scores.date_added 
FROM entities 

INNER JOIN scores 
ON entities.id = scores.entity_id 

WHERE entities.id in 
(select id from scores s2 where date_added = max(date_added) and s2.id = entities.id) 

ORDER BY scores.date_added DESC 
LIMIT 1 
+0

Votre sous-requête utilise une colonne (date_added) qui n'existe pas dans la table que vous interrogez. –

5

approche 1

SELECT entities.*, 
     scores.score, 
     scores.date_added 
FROM entities 

INNER JOIN scores 
ON entities.id = scores.entity_id 

WHERE scores.date_added = 
    (SELECT max(date_added) FROM scores where entity_id = entities.id) 
+0

Performance meilleure (de loin) si [scores] est indexé par [entity_id] – MatBailie

+0

Dans mes tests, cette solution semble retourner plusieurs lignes pour toute entité où plus d'un score a été ajouté pour cette entité à la même (dernière) date. – beporter

+0

De même, il ignorerait les lignes qui n'ont pas de score. – Coleman

3

approche 2

coût de la requête par rapport à lot:


SELECT entities.*, 
     scores.score, 
     scores.date_added 
FROM entities 

INNER JOIN scores 
ON entities.id = scores.entity_id 

inner join 
    (
    SELECT 
      entity_id, max(date_added) as recent_date 
    FROM scores 
    group by entity_id 
    ) as y on entities.id = y.entity_id and scores.date_added = y.recent_date 
9

Pour ajouter ma variation sur elle:

SELECT e.*, s1.score 
FROM entities e 
INNER JOIN score s1 ON e.id = s1.entity_id 
WHERE NOT EXISTS (
    SELECT 1 FROM score s2 WHERE s2.id > s1.id 
) 
+3

J'aime celui-ci! Au moins sur SQL Server, cela fonctionnera très vite. Maintenant, je suggère de changer l'INNER pour un LEFT JOIN, juste au cas où une nouvelle entité vient d'être ajoutée et le processus n'a pas encore été exécuté. –

+1

Pour une vitesse supplémentaire, vous pouvez mettre le test d'existence dans les conditions de jointure. Au moins sur SQL S. ils sont exécutés avant que le filtrage WHERE soit effectué, donc vous économiserez quelques millisecondes par ligne en supprimant votre recherche. –

+0

Hmmmm, WHERE le filtrage n'est pas nécessairement effectué après les clauses JOIN. En fait, ils peuvent être fait en premier, surtout si la clause WHERE filtre sur un INDEX ... – MatBailie

2

Je sais que c'est une vieille question, juste pensé que je rajouterais une approche personne n'a encore mentionné, Cross Apply ou Outer Apply. Ceux-ci sont disponibles dans SQL Server 2005 (le type de base de données ne sont pas marqués dans cette question) ou plus

En utilisant les tables temporaires

DECLARE @Entities TABLE(Id INT PRIMARY KEY, name NVARCHAR(MAX)) 
INSERT INTO @Entities 
VALUES (1, 'Apple'), (2, 'Orange'), (3, 'Banana'), (4, 'Cherry') 

DECLARE @Scores TABLE(Id INT PRIMARY KEY, Entity_Id INT, Score INT, Date_Added DATE) 
INSERT INTO @Scores 
VALUES (1,1,10,'2009-02-01'), 
(2,2,10,'2009-02-01'), 
(3,1,15,'2009-02-01'), 
(4,2,10,'2009-03-01'), 
(5,1,15,'2009-04-01'), 
(6,2,15,'2009-04-01'), 
(7,3,22,'2009-04-01') 

Vous pouvez utiliser

SELECT E.Id, E.name, S.Score, S.Date_Added 
FROM @Entities E 
CROSS APPLY 
(
    SELECT TOP 1 * 
    FROM @Scores Sc 
    WHERE Sc.Entity_Id = E.Id 
    ORDER BY sc.Score DESC 
) AS S 

pour obtenir les résultats souhaités . Le equivilent pour permettre aux entités sans scores seraient

SELECT E.Id, E.name, S.Score, S.Date_Added 
FROM @Entities E 
OUTER APPLY 
(
    SELECT TOP 1 * 
    FROM @Scores Sc 
    WHERE Sc.Entity_Id = E.Id 
    ORDER BY sc.Score DESC 
) AS S 
1

Vous pouvez aussi le faire aujourd'hui dans la plupart des SGBDR (Oracle, PostgreSQL, SQL Server) avec une requête en langage naturel en utilisant des fonctions de fenêtre telles que ROW_NUMBER:

SELECT id, name, score, date_added FROM (
SELECT e.id, e.name, s.score, s.date_added, 
ROW_NUMBER() OVER (PARTITION BY e.id ORDER BY s.date_added DESC) rn 
FROM Entities e INNER JOIN Scores s ON e.id = s.entity_id 
) tmp WHERE rn = 1; 

SQL Fiddle

Questions connexes