LEFT JOIN nur erste Reihe

137

Ich habe viele Threads darüber gelesen, nur die erste Zeile eines linken Joins zu erhalten, aber aus irgendeinem Grund funktioniert dies bei mir nicht.

Hier ist meine Struktur (natürlich vereinfacht)

Einspeisungen

id |  title | content
----------------------
1  | Feed 1 | ...

Künstler

artist_id | artist_name
-----------------------
1         | Artist 1
2         | Artist 2

feeds_artists

rel_id | artist_id | feed_id
----------------------------
1      |     1     |    1 
2      |     2     |    1 
...

Jetzt möchte ich die Artikel bekommen und mich nur dem ersten Künstler anschließen, und ich dachte an so etwas:

SELECT *
    FROM feeds 
    LEFT JOIN feeds_artists ON wp_feeds.id = (
        SELECT feeds_artists.feed_id FROM feeds_artists
        WHERE feeds_artists.feed_id = feeds.id 
    LIMIT 1
    )
WHERE feeds.id = '13815'

nur um nur die erste Zeile der feeds_artists zu bekommen, aber das funktioniert schon nicht.

Ich kann es TOPaufgrund meiner Datenbank nicht verwenden und ich kann die Ergebnisse nicht gruppieren, feeds_artists.artist_idda ich sie nach Datum sortieren muss (ich habe Ergebnisse erhalten, indem ich sie auf diese Weise gruppiert habe, aber die Ergebnisse waren nicht die neuesten).

Versuchte auch etwas mit OUTER APPLY - auch kein Erfolg. Um ehrlich zu sein, kann ich mir nicht wirklich vorstellen, was in diesen Reihen vor sich geht - wahrscheinlich der Hauptgrund, warum ich das nicht zum Laufen bringen kann.

LÖSUNG:

SELECT *
FROM feeds f
LEFT JOIN artists a ON a.artist_id = (
    SELECT artist_id
    FROM feeds_artists fa 
    WHERE fa.feed_id = f.id
    LIMIT 1
)
WHERE f.id = '13815'
KddC
quelle
Hier ist die Lösung stackoverflow.com/a/7588442/612987
Wasim A.
Sie sollten die Lösung als Antwort
angeben,
Ich denke du hast recht - ich habe es auch als Antwort hinzugefügt.
KddC

Antworten:

97

Wenn Sie davon ausgehen können, dass die Künstler-IDs mit der Zeit zunehmen, ist MIN(artist_id)dies die früheste.

Also versuchen Sie so etwas (ungetestet ...)

SELECT *
  FROM feeds f
  LEFT JOIN artists a ON a.artist_id = (
    SELECT
      MIN(fa.artist_id) a_id
    FROM feeds_artists fa 
    WHERE fa.feed_id = f.feed_id
  ) a
Matt Dodge
quelle
Danke für die schnelle Rückmeldung. Dies war nicht die genaue Antwort, hat mich aber auf den richtigen Weg gebracht. Ich habe immer versucht, beide auf derselben Ebene zu verbinden, anstatt die eine von der anderen abhängig zu machen. Vielen Dank, dass Sie mich auf den richtigen Weg geführt haben. Bearbeitet den ersten Beitrag
KddC
@KddC Anstatt die Frage zu ändern, um die Antwort hinzuzufügen, bearbeiten Sie diese Antwort oder geben Sie Ihre eigene Antwort ab.
PhoneixS
4
Wäre an dieser Stelle eine einfache Unterabfrage nicht besser? Denn jetzt haben Sie einen Join und eine Unterabfrage. Ich
frage
2
Unterabfrage ist zu langsam.
Sinux
1
@Sinux definieren "zu langsam". Dies hängt von der Anzahl der Datensätze und den vorgegebenen Anforderungen ab.
Beobachter
53

Version ohne Unterauswahl:

   SELECT f.title,
          f.content,
          MIN(a.artist_name) artist_name
     FROM feeds f
LEFT JOIN feeds_artists fa ON fa.feed_id = f.id
LEFT JOIN artists a ON fa.artist_id = a.artist_id
 GROUP BY f.id
Denis Khvorostin
quelle
2
Ich denke nicht, dass dies die erste Zeile sein wird (erste ID oder erste von etwas anderem), es wird nur zufällig eine unter den linken Verknüpfungszeilen ausgewählt.
Sinux
26
Diese Abfrage ist falsch. Es wird der "niedrigste" Künstlername ausgewählt, nicht der Name der ersten Künstler im Set.
Glapa
5
Falsch für die Frage ... aber genau das, was ich will. In meinem Fall möchte ich nur die erste ID von der Tabelle, an der ich teilnehme.
Drew Stephens
2
Das Problem hierbei ist, dass Sie Daten vermasselt haben, wenn Sie sich für COUNT oder SUM entscheiden. Das Problem mit Subselect ist, dass das Abrufen der Daten beim Erstellen einer temporären Tabelle schwieriger ist ... Ich wünschte, MySql könnte ein LIMIT auf der Ebene LEFT JOIN haben.
Shadoweb
Diese Lösung weist Leistungsprobleme auf. Verwenden Sie stattdessen die Min / Max-Lösung.
Sadegh PM
25

Die Antwort von @Matt Dodges hat mich auf den richtigen Weg gebracht. Nochmals vielen Dank für alle Antworten, die in der Zwischenzeit vielen Leuten geholfen haben. Habe es so gemacht:

SELECT *
FROM feeds f
LEFT JOIN artists a ON a.artist_id = (
    SELECT artist_id
    FROM feeds_artists fa 
    WHERE fa.feed_id = f.id
    LIMIT 1
)
WHERE f.id = '13815'
KddC
quelle
Dies ist eine Antwort, die nur für die erste Zeile funktioniert. Bricht, wenn keine f.idBedingung vorliegt.
Tajni
2

Ich habe etwas anderes verwendet (ich denke besser ...) und möchte es teilen:

Ich habe eine VIEW erstellt, die eine "Gruppen" -Klausel enthält

CREATE VIEW vCountries AS SELECT * PROVINCES GROUP BY country_code

SELECT * FROM client INNER JOIN vCountries on client_province = province_id

Ich möchte noch sagen, dass ich denke, dass wir diese Lösung machen müssen, WEIL WIR IN DER ANALYSE ETWAS FALSCHES GEMACHT HABEN ... zumindest in meinem Fall ... aber manchmal ist es billiger, dies zu tun, um alles neu zu gestalten ...

Ich hoffe, es hilft!

Ari Waisberg
quelle
Angenommen, es gibt mehrere Zeilen (Provinzen?) Für jede country_code, GROUP BYist das unangemessen. Siehe ONLY_FULL_GROUP_BY.
Rick James
Sie müssen keine Ansicht erstellen, die ausschließlich für den LEFT / INNER JOIN verwendet wird?! Kann eine Unterabfrage oder eine WITH x AS (Klausel verwendet werden?
Kiradotee
2

Basierend auf mehreren Antworten hier fand ich etwas, das für mich funktionierte und ich wollte verallgemeinern und erklären, was los ist.

Konvertieren:

LEFT JOIN table2 t2 ON (t2.thing = t1.thing)

zu:

LEFT JOIN table2 t2 ON (t2.p_key = (SELECT MIN(t2_.p_key) 
    FROM table2 t2_ WHERE (t2_.thing = t1.thing) LIMIT 1))

Die Bedingung, die t1 und t2 verbindet, wird von ONund in die innere Abfrage verschoben WHERE. Das MIN(primary key)oder LIMIT 1stellt sicher, dass nur 1 Zeile von der inneren Abfrage zurückgegeben wird.

Nachdem wir eine bestimmte Zeile ausgewählt haben, müssen wir ONder Zeile mitteilen, um welche es sich handelt. Aus diesem Grund ONwird der Primärschlüssel der verknüpften Tabelle verglichen.

Sie können mit der inneren Abfrage spielen (dh Reihenfolge + Limit), sie muss jedoch einen Primärschlüssel der gewünschten Zeile zurückgeben, der ONdie genaue Zeile angibt, der verbunden werden soll.

Oriadam
quelle
Hinweise: Es funktioniert auch nur mit LIMIToder nur MIN. Die ONBedingung muss sich auf dem Primärschlüssel der verknüpften Tabelle befinden, um Auswirkungen auf die Leistung zu vermeiden.
Oriadam
1

Ich möchte eine allgemeinere Antwort geben . Eine, die in jedem Fall funktioniert , wenn Sie nur das erste Element in einem LEFT JOIN auswählen möchten .

Sie können eine Unterabfrage verwenden, die GROUP_CONCATS entspricht, was Sie möchten (auch sortiert!), Dann einfach das GROUP_CONCAT-Ergebnis aufteilen und nur das erste Element nehmen, wie folgt ...

LEFT JOIN Person ON Person.id = (
    SELECT SUBSTRING_INDEX(
        GROUP_CONCAT(FirstName ORDER BY FirstName DESC SEPARATOR "_" ), '_', 1)
    ) FROM Person
);

Da wir DESC als ORDER BY- Option haben, wird eine Personen-ID für jemanden wie "Zack" zurückgegeben. Wenn wir jemanden mit dem Namen "Andy" wollten, würden wir ORDER BY FirstName DESC in ORDER BY FirstName ASC ändern .

Dies ist flink, da Sie damit die Möglichkeit haben, vollständig zu bestellen. Nach vielen Tests lässt es sich jedoch in einer Situation mit vielen Benutzern und vielen Daten nicht gut skalieren .

Es ist jedoch nützlich, um datenintensive Berichte für den Administrator auszuführen.

HoldOffHunger
quelle
Der GROUP_CONCATTrick ist gut, solange die Anzahl der Werte begrenzt ist. Das Standardlimit beträgt 1024 Zeichen.
Rick James