2014-05-13 17 views
6

Utknąłem z tworzeniem kwerendy MySQL. Bellow jest moją strukturą bazy danych.Nie można określić, co klauzula WHERE powinna używać JOIN MYSQL

autorzy (author_id i author_name)

Książki (book_id i BOOK_TITLE)

books_authors jest tabela Link (book_id i author_id)

wyniku wszystkich książek i autorów - http://i.stack.imgur.com/VLzXn.png

Potrzebuję dostać cały bo oks dla określonego autora, ale jeśli książka ma dwóch autorów, druga musi być również wyświetlana. Na przykład książka "Good Omens" z book_id = 2 ma dwóch autorów. Po uruchomieniu zapytania otrzymuję książki dla author_id = 1, ale nie mogę uwzględnić drugiego autora - "Neil Gaiman" w wyniku. Zapytanie jest:

SELECT * FROM books 
    LEFT JOIN books_authors 
     ON books.book_id=books_authors.book_id 
    LEFT JOIN authors 
     ON books_authors.author_id=authors.author_id 
WHERE books_authors.author_id=1 

A poniżej jest wynikiem:

http://i.stack.imgur.com/PypqZ.png

Mam nadzieję, że mnie rozumiesz i każda pomoc będzie mile widziane.

+0

Użyj podkwerenda generować listę numerów książek, a następnie listę wszystkich autorów powiązanych z tymi książkami ids/ – vogomatix

Odpowiedz

2

Nie trzeba podkwerenda dla tego:

SELECT * 
FROM book_authors ba 
    JOIN books b 
    ON b.book_id = ba.book_id 
    JOIN book_authors ba2 
    ON ba2.book_id = b.book_id 
    JOIN authors a 
    ON a.author_id = ba2.author_id 
WHERE ba.author_id = 1 
5

Musisz zmienić klauzulę WHERE wykonać podselekcji takiego:

SELECT b.*, a.* 
FROM books b 
LEFT JOIN books_authors ba ON ba.book_id = b.book_id 
LEFT JOIN authors  a ON a.author_id = ba.author_id 
WHERE b.book_id IN (
    SELECT book_id 
    FROM books_authors 
    WHERE author_id=1) 

Problem z zapytania jest to, że klauzula WHERE nie jest tylko filtrowanie książek otrzymujesz w zestawie wyników, ale także stowarzyszenia autorów książek.

Przy tym podzapytaniu najpierw użyjesz ID autora do filtrowania książek, a następnie użyjesz tych numerów książek, aby pobrać wszystkich powiązanych autorów.

Na marginesie, uważam, że propozycja zastąpienia ZŁĄCZEK ZEWNĘTRZNYCH z DOŁĄCZENIA WEWNĘTRZNE w tym konkretnym przypadku powinna mieć zastosowanie. Pierwsze LEFT OUTER JOIN na books_authors jest z pewnością bezużyteczne, ponieważ klauzula WHERE gwarantuje, że co najmniej jeden wiersz istnieje w tej tabeli dla każdego wybranego book_id. Drugie LEFT OUTER JOIN to prawdopodobnie bezużyteczne, ponieważ oczekuję, że author_id będzie kluczem podstawowym tabeli autorów, i spodziewam się, że tabela books_authors ma klucz obcy i ograniczenie NOT NULL na author_id ... co oznacza, że ​​powinieneś nie ma wiersza books_authors, który nie odwołuje się do konkretnego wiersza autorów.

Jeśli to prawda i potwierdzone, a następnie zapytanie powinno być:

SELECT b.*, a.* 
FROM books b 
JOIN books_authors ba ON ba.book_id = b.book_id 
JOIN authors  a ON a.author_id = ba.author_id 
WHERE b.book_id IN (
    SELECT book_id 
    FROM books_authors 
    WHERE author_id=1) 

Uwaga, INNER JOIN może równie dobrze być bardziej wydajny niż sprzężenia zewnętrzne w większości przypadków (dają silnikowi większy wybór, w jaki sposób wykonaj wiązanie i pobierz wynik). Dlatego powinieneś unikać OUTER JOINs, jeśli nie jest to absolutnie konieczne.

Dodałem aliasy i usunąłem zbędne kolumny z zestawu wyników.

+0

To już powiedziałem - prawie! :-) – vogomatix

+0

Tak ... Już odpowiedziałem, gdy pojawił się twój komentarz ... przepraszam za to – Frazz

+0

Nie martw się, mój komentarz był wadliwy i tak - Twoja odpowiedź jest poprawniejsza – vogomatix

0

Jesteś prawie blisko ... w zasadzie musisz zidentyfikować wszystkie unikalne numery książek, dla których autor_id =?. Następnie dołącz do tego ponownie za pomocą tabeli book_author, aby wszyscy autorzy powiązali te identyfikatory książek. Następnie dołącz do książek i autorów, aby uzyskać nazwiska książek i autorów.

Mam nadzieję, że po bardzo jasne w tej kwestii, ale jeśli to nie jest po prostu daj mi znać, a ja pomogę wyjaśnić bardziej szczegółowo

SELECT a.*, d.* FROM books as a 
    INNER JOIN (SELECT book_id FROM books_authors WHERE author_id=?) as b 
     ON a.book_id=b.book_id 
    INNER JOIN books_authors as c 
     ON b.book_id=c.book_id 
    INNER JOIN authors AS d 
     ON d.author_id = c.author_id 

Btw można również zorganizować to z WHERE EXISTS klauzuli . Nie sądzę, żebyś zauważyła znaczną różnicę w wydajności, ale po prostu FYI możesz spróbować, jeśli zajdzie taka potrzeba. Użyj opcji EXPLAIN, aby wyświetlić plan wykonania kwerendy. Jeśli jest to problematyczne, istnieją inne sposoby na skórze tego kota.

Pamiętaj też, aby zwracać uwagę na indeksy. Niezależnie od tego, czy użyjesz tu metody, czy metody opisanej przez Frazza, indeks złożony/kolumna-zmutowana/złożony może zrobić dla ciebie dużą różnicę. Oznacza to, że indeksowanie books_authors zarówno przez book_id i przez (author_id, book_id). Niezależnie od tego, czy powinieneś użyć dodatkowego sprzężenia, czy też podzapytania IN lub EXISTS ... wiele sposobów na skórze kota. Bez względu na to jednak, mając wielokolumnowego indeks books_authors może pomóc, zwłaszcza jeśli ta tabela jest duża

+1

mmmm ... to nie wydaje się dawać pożądanych rezultatów. http://sqlfiddle.com/#!2/b52d53/4 –

+0

Dobry połów. Kwestią jest LEFT JOIN. Zastanawiałeś się nad powiedzeniem, wiesz, nie jestem pewien, czy LEFT DOŁĄCZ w dobrym pomyśle. Jeśli potrzebujesz połączenia lewostronnego, użyj metody podkwerendy. Jeśli tego nie zrobisz, użyj tego, co jest szybsze lub co jest łatwiejsze do utrzymania (jeśli są równi wykonawcom) – evanv