Struggling with MySQL query using joins - mysql

I have 3 tables for storing information about books:
books
-----
id
title
authors
-------
id
name
books_to_authors
----------------
book_id
author_id
When a book's information is displayed, I then want to be able to select any other books by the same authors.
I have the current book id available from the first query, but I can't figure out where to start to achieve what I need as there can be multiple authors. Obviously with just one of them it would be simple, so I'm really struggling with this. Any help would be much appreciated!

I think this aught to do it. Just replace the ? with the book ID they are currently viewing and this will give you all the books by the same author.
SELECT b.*
FROM books b
INNER JOIN books_to_authors b2a ON b2a.book_id = b.id
WHERE b2a.author_id IN (
SELECT author_id FROM books_to_authors WHERE book_id = ?
)
If you want to exclude the book they are currently viewing, you can change the query like this:
SELECT b.*
FROM books b
INNER JOIN books_to_authors b2a ON b2a.book_id = b.id
WHERE b2a.author_id IN (
SELECT author_id FROM books_to_authors WHERE book_id = ?
)
AND b.id <> ?

$book_id = (your code for retrieving book_id);
$db_query = "SELECT b.*
FROM books b
INNER JOIN books_to_authors bta ON bta.book_id = b.id
WHERE bta.author_id IN (
SELECT author_id FROM books_to_authors WHERE book_id = ".$book_id."
)";
I presumed that you are using php. If I'm wrong, just use SQL query string, and ignore the rest...

You're looking for the query below. I see some solutions with subqueries and I'd highly recommend not using subqueries. They are slower than running 2 queries:
Having the book id you do SELECT author_id FROM books_to_authors WHERE book_id = '{$book_id}'
Get the author id and then run this:
SELECT books.id, books.title, authors.name FROM books RIGHT JOIN books_to_authors ON books_to_authors.book_id = books.id) RIGHT JOIN authors ON (authors.id = books_to_authors.author_id) WHERE authors.id = '{$author_id}'

Related

Understanding use of multiple SUMs with LEFT JOINS in mysql

Using the GROUP BY command, it is possible to LEFT JOIN multiple tables and still get the desired number of rows from the first table.
For example,
SELECT b.title
FROM books `b`
LEFT JOIN orders `o`
ON o.bookid = b.id
LEFT JOIN authors `a`
ON b.authorid = a.id
GROUP BY b.id
However, since behind the scenes MYSQL is doing a cartesian product on the tables, if you include more than one SUM command you get incorrect values based on all the hidden rows. (The problem is explained fairly well here.)
SELECT b.title,SUM(o.id) as sales,SUM(a.id) as authors
FROM books `b`
LEFT JOIN orders `o`
ON o.bookid = b.id
LEFT JOIN authors `a`
ON b.authorid = a.id
GROUP BY b.id
There are a number of answers on SO about this, most using sub-queries in the JOINS but I am having trouble applying them to this fairly simple case.
How can you adjust the above so that you get the correct SUMs?
Edit
Example
books
id|title|authorid
1|Huck Finn|1
2|Tom Sawyer|1
3|Python Cookbook|2
orders
id|bookid
1|1
2|1
3|2
4|2
5|3
6|3
authors
id|author
1|Twain
2|Beazley
2|Jones
The "correct answer" for total # of authors of the Python Cookbook is 2. However, because there are two joins and the overall dataset is expanded by the join on number of orders, SUM(a.id) will be 4.
You are correct that by joining multiple tables you would not get the expected results.
But in this case you should use COUNT() instead of SUM() and count the distinct orders or authors.
Also by your design you should count the names of the authors and not the ids of the table authors:
SELECT b.title,
COUNT(DISTINCT o.id) as sales,
COUNT(DISTINCT a.author) as authors
FROM books `b`
LEFT JOIN orders `o` ON o.bookid = b.id
LEFT JOIN authors `a` ON b.authorid = a.id
GROUP BY b.id, b.title
See the demo.
Results:
| title | sales | authors |
| --------------- | ----- | ------- |
| Huck Finn | 2 | 1 |
| Tom Sawyer | 2 | 1 |
| Python Cookbook | 2 | 2 |
When dealing with separate aggregates, it is good style to aggregate before joining.
Your data model is horribly confusing, making it look like a book is written by one author only (referenced by books.authorid), while this "ID" is not an author's ID at all.
Your main problem is: You don't count! We count with COUNT. But you are mistakenly adding up ID values with SUM.
Here is a proper query, where I am aggregating before joining and using alias names to fight confusion and thus enhance the query's readability and maintainability.
SELECT
b.title,
COALESCE(o.order_count, 0) AS sales,
COALESCE(a.author_count, 0) AS authors
FROM (SELECT title, id AS book_id, authorid AS author_group_id FROM books) b
LEFT JOIN
(
SELECT id as author_group_id, COUNT(*) as author_count
FROM authors
GROUP BY id
) a ON a.author_group_id = b.author_group_id
LEFT JOIN
(
SELECT bookid AS book_id, COUNT(*) as order_count
FROM orders
GROUP BY bookid
) o ON o.book_id = b.book_id
ORDER BY b.title;
i don't think that your query would work like you eexspected.
Assume one book could have 3 authors.
For Authors:
So you would have three rows for that book in your books table,each one for every Author.
So a
SUM(b.authorid)
gives you the correct answer in your case.
For Orders:
you must use a subselect like
LEFT JOIN (SELECT SUM(id) o_sum,bookid FROM orders GROUP BY bookid) `o`
ON o.bookid = b.id
You should really reconsider your approach with books and authors.

Performing a Right Join?

Books
id | title
Articles
id | title | book_id
I need to get all articles that are from a particular book:
SELECT * FROM articles WHERE book_id = 10
With this query I also need to get the parents book title.
I presume I need a right join for this:
SELECT books.title, articles.* FROM books
RIGHT JOIN articles ON book.id = articles.book_id WHERE books.id = 10
Is this correct? Or is a right join not the way to go?

MySQL how to join 3 tables when one doesn't have a common key

I have three tables and I need to join as follows.
INVENTORY
BOOK_CODE, BRANCH_NUM, ON_HAND
BOOK
BOOK_CODE, TITLE
AUTHOR
AUTHOR_NUM, AUTHOR_LAST
Basically, I need to select the author name, book title, and on-hand count for all books from Branch 4 (in inventory). The problem is, the AUTHOR table has to be reached through a fourth table.
WROTE
BOOK_CODE, AUTHOR_NUM
If all the tables had that BOOK_CODE, I could do it easy, but I can't figure out how to jam it all into one query. I've tried the following:
SELECT TITLE, AUTHOR_LAST, ON_HAND
FROM BOOK, AUTHOR, INVENTORY
WHERE BOOK.BOOK_CODE = INVENTORY.BOOK_CODE
AND AUTHOR_NUM IN
(SELECT AUTHOR_NUM
FROM WROTE
WHERE WROTE.BOOK_CODE = INVENTORY.BOOK_CODE)
AND BRANCH_NUM='4';
But this returns the wrong fields, so I don't know what I'm doing wrong.
You can "chain" your joins:
SELECT title, author_last, on_hand
FROM book b
JOIN inventory i ON b.book_code = i.book_code
JOIN wrote w ON b.book_code = w.book_code
JOIN author a ON a.author_num = w.author_num
WHERE branch_num = 4
You should try Joining it will be Helpful rather that Sub query
SELECT a.AUTHOR_LAST,d.TITLE,c.ON_HAND FROM WROTE a INNER JOIN AUTHOR b
ON a.AUTHOR_NUM=b.AUTHOR_NUM INNER JOIN INVENTORY c
ON c.BOOK_CODE=a.BOOK_CODE INNER JOIN BOOK d
ON a.BOOK_CODE=d.BOOK_CODE
WHERE c.BRANCH_NUM=4
Try like this

Fetching data from many-to-many relationships

I want to fetch the title from book table and the subject name from subject table for all the books of a certain author
Here is the tables relationships
I have tried to select the title and the subject from the tables but I can't get it to work with the many to many relationship
SELECT book_title,subject_name FROM book,subject WHERE $subject_ID = subject_ID INNER JOIN book_author ON author_ID = '$author_ID'
I thought about making two separate queries, if someone could help me on that I would be really glad.
Try this one:
SELECT book_title, subject_name
FROM Book
INNER JOIN Book_Author ON Book.book_ISBN = Book_Author.book_ISBN
INNER JOIN Author ON Book_Author.author_ID = Author.author_ID
INNER JOIN Subject ON Subject.subject_ID = Book.subject_ID
WHERE author_lastname = [whatever];
And nice job posting the model :)
Edit to match the exact need:
SELECT book_title, subject_name
FROM Book
INNER JOIN Book_Author ON Book.book_ISBN = Book_Author.book_ISBN
INNER JOIN Subject ON Subject.subject_ID = Book.subject_ID
WHERE author_ID = '11';
And by the way, you're having an "Column 'author_ID' in where clause is ambiguous" because this column appears both in Book_Author and Author. That's why you have to prefix it with the table name :)

How do I link tables with id from a list stored as XML in a column

Hopefully the question explains it well. I have a DB for a Library. Since they can be used many times, and contains more data than just a name, I have a table for Authors. Then there's a table for Books. I have no problem linking Authors to Books via a column called Author_id.
What I'm trying to do is have a column called Author_IDs that contains a list of id's, since a book can have multiple IDs. In the Author_IDs column I have:
<id>3478</id>
<id>6456</id>
Using the ExtractValue function in MySQL I can link the table with one or the other id using:
WHERE Author.id = ExtractValue(Book.Author_IDs,"/id[2]") // to get the second ID.
My question is, I want to be able to automatically display all of the authors of a book, but don't know how to link to it more than once, without looping. How can I get the results to show me all of the authors?
(Or is there a better way to accomplish this?)
Firstly, I have to vote against your storage method. Storing data as xml inside a mysql column should be avoided if possible. If you use a normal approach you will find this problem to be much easier.
Create a table:
book_authors
book_id author_id
------- ---------
1 1
1 2
1 3
2 2
2 4
Then to get all of the authors associated with a certain book it's a simple query.
Select
b.book_id,
b.book_name,
GROUP_CONCAT(a.author_name) AS 'authors'
FROM
book_authors ba LEFT JOIN
books b ON ba.book_id = b.book_id LEFT JOIN
authors a ON ba.author_id = a.author_id
GROUP BY
ba.book_id
Not sure I understand completely. Could something like this do the trick?
select a.* from tblBooks b
left join tblAuthors a on (b.authors = concat('%', a.id, '%')
where b = 'book id';
I would have done it like this
Structure
tblBooks
--------
book_id
book_name
tblAuthors
----------
author_id
author_name
tblBooksToAuthors
-----------------
id
book_id
author_id
Query
select a.*
from tblAuthors a
left join tblBooksToAuthors b2a on (b2a.author_id = a.author_id)
where b2a.book_id = {your book id};