LEWY DOŁĄCZ tylko do pierwszego rzędu

138

Czytałem wiele wątków o uzyskiwaniu tylko pierwszego rzędu lewego łączenia, ale z jakiegoś powodu to nie działa dla mnie.

Oto moja struktura (oczywiście uproszczona)

Kanały

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

Artyści

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

feeds_artists

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

Teraz chcę dostać artykuły i dołączyć tylko do pierwszego Artysty i pomyślałem o czymś takim:

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'

tylko po to, aby uzyskać tylko pierwszy wiersz feeds_artists, ale już to nie działa.

Nie mogę użyć z TOPpowodu mojej bazy danych i nie mogę grupować wyników według feeds_artists.artist_iddaty (mam wyniki, grupując je w ten sposób, ale wyniki nie były najnowsze)

Próbowałem też czegoś z OUTER APPLY - też bez sukcesu. Szczerze mówiąc, nie mogę sobie wyobrazić, co się dzieje w tych rzędach - prawdopodobnie jest to największy powód, dla którego nie mogę tego zrobić.

ROZWIĄZANIE:

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
źródło
tutaj jest rozwiązanie stackoverflow.com/a/7588442/612987
Wasim A.
Powinieneś podać rozwiązanie jako odpowiedź, abyśmy mogli je zagłosować i aby było bardziej widoczne dla przyszłych odwiedzających
fguillen
Chyba masz rację - dodałem to również jako odpowiedź.
KddC

Odpowiedzi:

97

Jeśli możesz założyć, że identyfikatory wykonawców rosną w czasie, to MIN(artist_id)będzie najwcześniej.

Więc spróbuj czegoś takiego (niesprawdzone ...)

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
źródło
Dziękuję za szybką odpowiedź. To nie była dokładna odpowiedź, ale całkowicie mnie na właściwej drodze. Zawsze starałem się łączyć oba na tym samym poziomie, zamiast uzależniać jedno od drugiego. Bardzo dziękuję za poprowadzenie mnie właściwą drogą.
Edytował
4
Czy w tym momencie proste zapytanie podrzędne nie byłoby lepsze? Ponieważ teraz masz sprzężenie i pod-zapytanie. Tylko pytam, bo szukam rozwiązania tego samego problemu :)
galdikas
2
podzapytanie jest zbyt wolne.
Sinux
1
@Sinux definiuje „zbyt wolno”. Zależy to od liczby rekordów i określonych wymagań.
obserwator
1
To nie zadziała! Zapytanie podrzędne NIE pozwala na przekazanie pola z zapytania nadrzędnego !!!
Thư Sinh
53

Wersja bez podselekcji:

   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
źródło
2
Nie sądzę, żeby to był pierwszy wiersz (pierwszy identyfikator lub pierwszy z czegoś innego), po prostu losowo wybierz jeden z lewego wierszy łączenia.
Sinux
26
To zapytanie jest błędne. Wybierze „najniższą” nazwę wykonawcy, a nie nazwiska pierwszych artystów w zestawie.
Glapa,
5
Źle co do pytania… ale dokładnie to, czego chcę. W moim przypadku chcę tylko pierwszy identyfikator ze stołu, do którego się przyłączam.
Drew Stephens,
2
Problem polega na tym, że jeśli zdecydujesz się zrobić COUNT lub SUM, zepsujesz dane. Problem z Subselect polega na tym, że pobieranie danych jest trudniejsze, ponieważ utworzył tabelę tymczasową ... Chciałbym, aby MySql miał LIMIT na poziomie LEFT JOIN.
Shadoweb,
To rozwiązanie ma problem z wydajnością. Zamiast tego użyj rozwiązania Min / Max.
Sadegh PM
25

Odpowiedź @Matt Dodges postawiła mnie na właściwej drodze. Jeszcze raz dziękuję za wszystkie odpowiedzi, które w międzyczasie pomogły wielu chłopakom. Działa tak:

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
źródło
To jest odpowiedź działająca tylko dla pierwszego rzędu. Przerwy, gdy nie ma f.idwarunków.
Tajni
2

Użyłem czegoś innego (myślę, że lepiej ...) i chcę się tym podzielić:

Stworzyłem VIEW, który ma klauzulę „group”

CREATE VIEW vCountries AS SELECT * PROVINCES GROUP BY country_code

SELECT * FROM client INNER JOIN vCountries on client_province = province_id

Chcę jeszcze powiedzieć, że myślę, że musimy zrobić to rozwiązanie, PONIEWAŻ W ANALIZIE ZROBILIŚMY COŚ NIEPRAWIDŁOWEGO ... przynajmniej w moim przypadku ... ale czasami jest to tańsze, aby wszystko przeprojektować ...

Mam nadzieję, że to pomoże!

Ari Waisberg
źródło
Zakładając, że istnieje wiele rzędów (prowincji?) Dla każdego country_code, GROUP BYjest to niewłaściwe. Zobacz ONLY_FULL_GROUP_BY.
Rick James,
Nie musisz tworzyć widoku, który będzie używany wyłącznie do LEFT / INNER JOIN ?! Czy można użyć podzapytania lub WITH x AS (klauzuli?
kiradotee
2

Opierając się na kilku odpowiedziach tutaj, znalazłem coś, co zadziałało dla mnie i chciałem uogólnić i wyjaśnić, co się dzieje.

konwertować:

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

do:

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

warunek, który łączy t1 i t2, jest przenoszony z ONi do zapytania wewnętrznego WHERE. MIN(primary key)i LIMIT 1zapewnia, że tylko jeden rząd jest zwracany przez zapytanie wewnętrznej.

po wybraniu jednego konkretnego wiersza musimy powiedzieć, ONktóry to wiersz. dlatego ONwłaśnie porównuje klucz podstawowy połączonej tabeli.

możesz grać z wewnętrznym zapytaniem (tj. zamówienie + limit), ale musi ono zwrócić jeden klucz podstawowy żądanego wiersza, który wskaże ONdokładny wiersz do połączenia.

oriadam
źródło
Uwagi: będzie działać również z samym LIMITlub tylko MIN. ONwarunek musi być na dołączył tabeli klucz podstawowy wpływ na wydajność uniknąć.
oriadam
1

Chcę udzielić bardziej ogólnej odpowiedzi . Taki, który poradzi sobie z każdym przypadkiem, gdy chcesz wybrać tylko pierwszy element w LEFT JOIN .

Możesz użyć podzapytania, które GROUP_CONCATS to, co chcesz (posortowane też!), A następnie po prostu podziel wynik GROUP_CONCAT i weź tylko jego pierwszy element, tak jak to ...

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

Ponieważ mamy DESC jako naszą opcję ORDER BY , to zwróci identyfikator osoby dla kogoś takiego jak „Zack”. Gdybyśmy chcieli kogoś o imieniu „Andy”, zmienilibyśmy ORDER BY FirstName DESC na ORDER BY FirstName ASC .

Jest to zwinne, ponieważ dzięki temu moc zamawiania jest całkowicie w Twoich rękach. Ale po wielu testach nie będzie dobrze skalować w sytuacji z dużą liczbą użytkowników i dużą ilością danych.

Jest to jednak przydatne do generowania raportów dla administratora zawierających duże ilości danych.

HoldOffHunger
źródło
Ta GROUP_CONCATsztuczka jest dobra, o ile liczba wartości jest ograniczona. Domyślny limit to 1024 znaków.
Rick James,