Performing a Right Join? - mysql

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?

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.

How to get articles which are part of the same group

I want to retrieve variants of the same article which are part of the same group. DBM is MySQL 5.7.
There are 2 tables:
articles
articles_group
Table articles has the fields:
article_id | title
1 first product
2 second prod
3 3rd prod
4 4th example
Table articles_groups:
group_id | article_id
1 1
1 2
1 3
2 4
In this example I would like to retrieve all other articles which are in the same group as article 1. So that would be article 2 and 3.
My best shot, but somehow pretty complex:
SELECT
art.article_id,
model
FROM
articles art
INNER JOIN
articles_group art_g ON art.article_id = art_g.article_id
WHERE art_g.group_id = (
SELECT ag.group_id
FROM articles a
INNER JOIN articles_group ag ON a.article_id = ag.article_id
WHERE a.article_id = 1
)
How can I retrieve all other articles which belong to the same group of given article in an easy way? I can still change the shema if there is a better setup.
Let's say your given article is 1. To get all articles in same group of given article, you can use subquery to get group_id of given article. Then use outer query to get all articles in same group.
SELECT a.article_id, a.title
FROM articles a
JOIN articles_groups g ON a.ref_id = g.ref_id
WHERE g.group_id = (
SELECT g.group_id
FROM articles a
JOIN articles_groups g ON a.ref_id = g.ref_id
WHERE a.article_id = '1'
)

Where clause on a string rather than id

I need to get all articles out of my database if they fall under a category or sub category.
articles:
id | title | content | category_id (fk)
categories
id | title | parent_id
1 toys 1
2 trains 1
3 pets 3
I perform:
SELECT * FROM categories LEFT JOIN articles ON categories.id = articles.category_id WHERE categories.id = ? OR WHERE categories.parent_id = ?
The above works, but now I want to use the category title instead of an id. So something like:
SELECT * FROM categories LEFT JOIN articles ON categories.id = articles.category_id WHERE **categories.title** = ? OR WHERE ??? not sure how to handle this bit
But im not sure how to handle the OR WHERE, as I don't know the categories id value.
Is there a way to do this without performing a category id lookup query first?
You could, first, get a list of categories that are child categories of a parent category and the parent category, then, join to find the matching articles.
SELECT * FROM(
SELECT *
FROM categories
WHERE title = 'toys'
UNION
select a.*
FROM categories a
JOIN categories b ON (a.parent_id = b.id)
WHERE b.title = 'toys'
) c
JOIN articles ON (c.id = articles.category_id);
SELECT * FROM categories c
LEFT JOIN articles ON c.id = articles.category_id
WHERE **c.title** = ? OR
c.title IN (select title from categories ca where c.title = ? AND ca.id = c.parent_id)
You can control if title of row's parent match whether or not matches with special title. There might be more efficient ways but this design is very similar to yours.

Select records if the count of related records is less than a number

I have two tables, Article and Comments.
Now I want to know the number of articles which have less than 5 comments.
How can I do that?
Articles:
id | author_id | title | cotent
Comments:
id | article_id | author_id | content
Edit: the original question is about larger than, but now I actually want less than (e.g., <5). If there is no comment record for an article, it seems this article is not listed.
Join both table to get the comments of articles and then group it by article id to know comment count of each article.
Try out this:
select art.id,count(*) as comment_count
from articles art
inner join comments com on art.id=com.article_id
group by art.id
having comment_count>5
For articles having comments <= 5:
select art.id,count(*) as comment_count
from articles art
left join comments com on art.id=com.article_id
group by art.id
having comment_count<=5
Try this
SELECT a.id, COUNT(*)
FROM
articles a
LEFT OUTER JOIN comments c ON a.id=c.article_id
GROUP BY a.id
HAVING COUNT(*) > 5
this will return id of all articles having more than 5 comments, with their respective comment count

Struggling with MySQL query using joins

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}'