J'ai des données qui ressemblent à ceci:
entities
id name
1 Apple
2 Orange
3 Banana
Périodiquement, un processus se déroulera et attribuera un score à chaque entité. Le processus génère les données et les ajoute à une table de scores comme suit:
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 pouvoir sélectionner toutes les entités ainsi que le score enregistré le plus récent pour chacune, ce qui donne 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 d'une seule entité en 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 ne sais pas comment sélectionner la même chose pour toutes les entités. Peut-être que ça me regarde en face?
Merci de bien vouloir prendre le temps.
Merci pour les bonnes réponses. Je vais lui donner quelques jours pour voir si une solution préférée bouillonne, puis je sélectionnerai la réponse.
MISE À JOUR: J'ai essayé plusieurs des solutions proposées, le principal problème auquel je suis confronté maintenant est que si une entité n'a pas encore de score généré, elles n'apparaissent pas dans la liste.
À quoi ressemblerait le SQL pour garantir que toutes les entités sont retournées, même si aucun score n'a encore été publié?
MISE À JOUR: réponse sélectionnée. Merci tout le monde!
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;
Juste pour ajouter ma variation à ce sujet:
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
)
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)
approche 2
coût de la requête par rapport au lot:
approche 1 : 22% - plus rapide
approche 2: 22% - plus rapide
approche : 27%
approche 4 : 27%
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
Je sais que c'est une vieille question, je pensais simplement ajouter une approche que personne n'a encore mentionnée, Cross Apply
ou Outer Apply
. Ceux-ci sont disponibles dans SQL Server 2005 (le type de base de données n'est pas balisé dans cette question) Ou supérieur
Utilisation des 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 pourriez 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. L'équivilent à autoriser des entités sans scores serait
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
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
Vous pouvez également le faire aujourd'hui dans la plupart des SGBDR (Oracle, PostgreSQL, SQL Server) avec une requête naturelle 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;