2009-01-30 6 views
49

Ich habe Daten, die wie folgt aussehen:Wie verknüpfe ich die letzte Zeile in einer Tabelle mit einer anderen Tabelle?

entities 
id   name 
1   Apple 
2   Orange 
3   Banana 

In regelmäßigen Abständen wird ein Prozess ausgeführt und eine Punktzahl zu jeder Einheit geben. Der Prozess erzeugt die Daten und fügt sie zu einem Tisch Partituren wie so:

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 

ich alle Einheiten in der Lage sein wollen, wählen Sie zusammen mit den neuesten aufgezeichnet Punktzahl für jede in einigen Daten wie diese resultierende:

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

kann ich die Daten für eine einzelne Einheit erhalten diese Abfrage mit:

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 

Aber ich bin ratlos, wie das gleiche für alle Objekte auszuwählen. Vielleicht starrt es mich an?

Vielen Dank, dass Sie sich die Zeit genommen haben.

Danke für die tollen Antworten. Ich werde es ein paar Tage geben, um zu sehen, ob eine bevorzugte Lösung aufbläht, dann wähle ich die Antwort aus.

UPDATE: Ich habe mehrere der vorgeschlagenen Lösungen ausprobiert, das Hauptproblem, mit dem ich es jetzt zu tun habe, ist, dass wenn eine Entität noch keinen generierten Score hat, sie nicht in der Liste erscheinen.

Wie sieht die SQL aus, um sicherzustellen, dass alle Entitäten zurückgegeben werden, auch wenn sie noch keine Punktzahl haben?

UPDATE: Antwort ausgewählt. Danke allen!

Antwort

60

Ich mache es so:

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

Dank Bill, habe ich mich letztendlich mit dieser Lösung begnügt, aber den INNER JOIN gegen einen LINKEN JOIN getauscht, um Entitäten einzubinden, die noch keine Scores haben. – GloryFish

+1

Ich mag diese Lösung, ich benutze auch das LINKE JOIN. Wie würden Sie empfehlen, sich mit einem Gleichstand zu befassen, falls es zwei Punkte für dasselbe Unternehmen mit demselben Datum gibt? – russds

+0

@russds, verwenden Sie eine andere Spalte, um Beziehungen zu lösen. –

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

Ihre Unterabfrage verwendet eine Spalte (date_added), die in der von Ihnen abgefragten Tabelle nicht vorhanden ist. –

5

Ansatz 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

Leistung am besten, wenn [Scores] durch [entity_id] indexiert wird – MatBailie

+0

In meinen Tests scheint diese Lösung mehrere Zeilen für Entitäten zurückzugeben, bei denen mehr als eine Punktzahl für diese Entität am selben (neuesten) Datum hinzugefügt wurde. – beporter

+0

Auch würde es Zeilen überspringen, die keine Punktzahl haben. – Coleman

3

Ansatz 2

Abfrage Kosten im Vergleich zu Charge:

012.351.
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

Nur meine Variante es hinzuzufügen:

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

Ich mag diesen! Zumindest auf SQL Server wird dies sehr schnell laufen. Jetzt würde ich vorschlagen, den INNER für einen LINKEN JOIN zu ändern, nur für den Fall, dass gerade eine neue Entität hinzugefügt wurde und der Prozess noch nicht ausgeführt wurde. –

+1

Für zusätzliche Geschwindigkeit könnten Sie den Existenztest als Teil der Fügebedingungen setzen. Zumindest in SQL S. Sie werden vor der WHERE-Filterung ausgeführt, sodass Sie einige Millisekunden pro Zeile sparen, indem Sie Ihre Suche dort einschränken. –

+0

Hmmmm, WHERE-Filterung wird nicht unbedingt nach JOIN-Klauseln durchgeführt. Tatsächlich können sie zuerst gemacht werden, besonders wenn die WHERE-Klausel auf einen INDEX filtert ... – MatBailie

2

Ich weiß, dass dies eine alte Frage ist, nur dachte ich niemand einen Ansatz hinzufügen würde noch erwähnt, Cross Apply oder Outer Apply. Diese sind in SQL Server 2005 (der Datenbanktyp ist in dieser Frage nicht markiert) oder höher

Mit den temporären Tabellen

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') 

Sie

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 

verwenden konnten die gewünschten Ergebnisse zu erhalten . Die equivilent ermöglichen Entitäten ohne Noten wären

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

Sie können auch diese heute tun in den meisten RDBMS (Oracle, PostgreSQL, SQL Server) mit einer natürlichen Abfrage mit Fensterfunktionen wie 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