478 Stimmen

SQL Join: Auswählen der letzten Datensätze in einer eins-zu-viele Beziehung

Angenommen, ich habe eine Tabelle mit Kunden und eine Tabelle mit Einkäufen. Jeder Einkauf gehört zu einem Kunden. Ich möchte eine Liste aller Kunden zusammen mit ihrem letzten Kauf in einer SELECT-Anweisung erhalten. Was ist die beste Vorgehensweise? Irgendwelche Ratschläge zum Erstellen von Indizes?

Bitte verwenden Sie diese Tabellen-/Spaltennamen in Ihrer Antwort:

  • Kunde: id, name
  • Kauf: id, customer_id, item_id, date

Und in komplizierteren Situationen, wäre es (leistungsbedingt) vorteilhaft, die Datenbank zu denormalisieren, indem man den letzten Kauf in die Kundentabelle aufnimmt?

Wenn die (Kauf-) id garantiert nach Datum sortiert ist, können die Anweisungen durch die Verwendung von etwas wie LIMIT 1 vereinfacht werden?

2 Stimmen

Ja, es könnte sich lohnen, zu denormalisieren (wenn es die Leistung erheblich verbessert, was Sie nur durch Testen beider Versionen herausfinden können). Aber die Nachteile der Denormalisierung sind normalerweise das Vermeiden wert.

3 Stimmen

34voto

Mathee Punkte 635

Ich habe diesen Thread als Lösung für mein Problem gefunden.

Aber als ich sie ausprobiert habe, war die Leistung schlecht. Unten finden Sie meinen Vorschlag für eine bessere Leistung.

Mit MaxDates als (
SELECT  customer_id,
                MAX(date) MaxDate
        FROM    purchase
        GROUP BY customer_id
)

SELECT  c.*, M.*
FROM    customer c INNER JOIN
        MaxDates as M ON c.id = M.customer_id 

Hoffentlich wird das hilfreich sein.

1 Stimmen

Um nur 1 zu erhalten, habe ich top 1 verwendet und nach MaxDate absteigend geordnet

2 Stimmen

Dies ist eine einfache und unkomplizierte Lösung, in MEINEM Fall (viele Kunden, wenige Einkäufe) 10 % schneller als die Lösung von @Stefan Haberl und mehr als zehnmal besser als die akzeptierte Antwort.

1 Stimmen

Toller Vorschlag, gemeinsame Tabellenausdrücke (CTE) zu verwenden, um dieses Problem zu lösen. Dies hat die Leistung von Abfragen in vielen Situationen dramatisch verbessert.

13voto

Rahul Murari Punkte 399

Versuch es mal, es wird helfen.

Ich habe das in meinem Projekt verwendet.

SELECT 
*
FROM
customer c
OUTER APPLY(SELECT top 1 * FROM purchase pi 
WHERE pi.customer_id = c.Id order by pi.Id desc) AS [LetzterKaufspreis]

0 Stimmen

Woher stammt das Pseudonym "p"?

1 Stimmen

Dies funktioniert nicht gut.... dauerte eine Ewigkeit, während andere Beispiele hier nur 2 Sekunden für das Datenset brauchten, das ich habe....

0 Stimmen

Dies war die leistungsstärkste Option für meinen Datensatz.

7voto

DanimalReks Punkte 165

Ich brauchte, was du brauchtest, wenn auch viele Jahre später, und probierte die beiden beliebtesten Antworten aus. Diese brachten nicht die gewünschten Früchte. Also das ist, was ich anzubieten habe... Zur Klarheit habe ich einige Namen geändert.

SELECT 
  cc.pk_ID AS pk_Customer_ID, 
  cc.Customer_Name AS Customer_Name, 
  IFNULL(pp.pk_ID, '') AS fk_Purchase_ID,
  IFNULL(pp.fk_Customer_ID, '') AS fk_Customer_ID,
  IFNULL(pp.fk_Item_ID, '') AS fk_Item_ID,
  IFNULL(pp.Purchase_Date, '') AS Purchase_Date
FROM customer cc
LEFT JOIN purchase pp ON (
  SELECT zz.pk_ID 
  FROM purchase zz 
  WHERE cc.pk_ID = zz.fk_Customer_ID 
  ORDER BY zz.Purchase_Date DESC LIMIT 1) = pp.pk_ID
ORDER BY cc.pk_ID;

4 Stimmen

Vielen Dank Bruder. Das funktioniert perfekt.

2 Stimmen

Ich habe eine Bedingung, bei der ich viele Tabellen zusammenführen muss und es gibt mindestens 2, bei denen ich eine Eins-zu-Viele-Beziehung verwendet habe. Dies hat tatsächlich mein Problem gelöst.

7voto

celsowm Punkte 496

Auf SQL Server könnten Sie verwenden:

SELECT *
FROM customer c
INNER JOIN purchase p on c.id = p.customer_id
WHERE p.id = (
    SELECT TOP 1 p2.id
    FROM purchase p2
    WHERE p.customer_id = p2.customer_id
    ORDER BY date DESC
)

SQL Server Beispiel: http://sqlfiddle.com/#!18/262fd/2

Auf MySQL könnten Sie verwenden:

SELECT c.name, date
FROM customer c
INNER JOIN purchase p on c.id = p.customer_id
WHERE p.id = (
    SELECT p2.id
    FROM purchase p2
    WHERE p.customer_id = p2.customer_id
    ORDER BY date DESC
    LIMIT 1
)

MySQL Beispiel: http://sqlfiddle.com/#!9/202613/7

5voto

Mark Punkte 7074

Auf SQLite getestet:

SELECT c.*, p.*, max(p.date)
FROM customer c
LEFT OUTER JOIN purchase p
ON c.id = p.customer_id
GROUP BY c.id

Die max()-Aggregatfunktion stellt sicher, dass der neueste Kauf aus jeder Gruppe ausgewählt wird (geht davon aus, dass die Datumsspalte in einem Format vorliegt, bei dem max() das neueste Datum zurückgibt - was normalerweise der Fall ist). Wenn Sie Käufe mit demselben Datum behandeln möchten, können Sie max(p.date, p.id) verwenden.

In Bezug auf Indizes würde ich einen Index auf den Kauf mit (customer_id, date, [alle anderen Kaufspalten, die Sie in Ihrem SELECT zurückgeben möchten]) verwenden.

Das LEFT OUTER JOIN (im Gegensatz zum INNER JOIN) stellt sicher, dass auch Kunden, die nie einen Kauf getätigt haben, einbezogen werden.

2 Stimmen

Wird in t-sql nicht funktionieren, da das select c.* Spalten enthält, die nicht in der group by-Klausel enthalten sind.

2 Stimmen

Ich finde auch, dass dies in SQLite funktioniert. Ich habe die Dokumentation durchsucht (die äußerst umfassend ist), um etwas zu finden, das besagt, dass es funktionieren sollte, konnte aber nichts finden. Daher gibt es keine Garantie dafür, dass es in zukünftigen Updates funktioniert (es sei denn, du findest etwas, das ich übersehen habe).

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X