Domanda

I'm stuck with creating a MySQL query. Below is my database structure.

authors (author_id and author_name)

books (book_id and book_title)

books_authors is the link table (book_id and author_id)

Result of all books and authors:

img

I need to get all the books for certain author, but if a book has 2 authors the second one must be displayed also. For example the book "Good Omens" with book_id=2 has two authors. When I run the query I get the books for the author_id=1 but I can not include the second author - "Neil Gaiman" in the result. The query is:

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

And below is the result:

È stato utile?

Soluzione 2

You don't need a subquery for this:

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

Altri suggerimenti

You need to change the WHERE clause to execute a subselect like this:

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)

The problem with your query is that the WHERE clause is not only filtering the books you are getting in the result set, but also the book-author associations.

With this subquery you first use the author id to filter books, and then you use those book ids to fetch all the associated authors.

As an aside, I do think that the suggestion to substitute the OUTER JOINs with INNER JOINs in this specific case should apply. The first LEFT OUTER JOIN on books_authors is certainly useless because the WHERE clause guarantees that at least one row exists in that table for each selected book_id. The second LEFT OUTER JOIN is probably useless as I expect the author_id to be primary key of the authors table, and I expect the books_authors table to have a foreign key and a NOT NULL constraint on author_id... which all means you should not have a books_authors row that does not reference a specific authors row.

If this is true and confirmed, then the query should be:

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)

Notice that INNER JOINs may very well be more efficient than OUTER JOINs in most cases (they give the engine more choice on how to execute the stament and fetch the result). So you should avoid OUTER JOINs if not strictly necessary.

I added aliases and removed the redundant columns from the result set.

You're pretty close... basically you need to identify all unique book ids for which author_id = ?. Then join that with the book_author table again to get all of the authors associate with those book ids. Then join to books and authors to get your book and author names.

Hopefully the following is very clear in this regard, but if it's not just let me know and I'll help explain it in more detail

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 you could also structure this with a WHERE EXISTS clause. I don't think you'll see much of a performance difference either way, but just FYI you can try that if need be. Use EXPLAIN to view the execution plan for the query. If it's problematic, there are other ways to skin this cat.

Also, make sure you pay attention to indices. Whether you use the method here, or the method described by Frazz, a compound/mutli-column/complex index may make a big difference for you. That is, consider indexing books_authors by both book_id and by (author_id, book_id). Whether you should use an additional join or an IN or an EXISTS subquery... lots of ways to skin the cat. No matter what, though, having a multicolumn index on books_authors is likely to help you out, especially if this table is large

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top