Super complicated SQL query - mysql

Have an article model, each article has an author and publisher (both are tables). A user can follow authors and publishers.
User -> Follow -> Author or Publisher -> Article
I want to find all the articles by the authors and publishers they are following.
SELECT articles.*, articles2.* FROM follows
INNER JOIN articles ON follows.author_id = articles.author_id
INNER JOIN articles AS articles2 ON follows.publisher_id = articles2.publisher_id
WHERE follows.user_id = 1
Can I get all the articles into 1 query? If so how? If not can I combine two queries and then order them?

select a.* from follows f
inner join articles a on (a.author_id = f.author_id or a.publisher_id=f.publisher_id)
where f.user_id = 1

SELECT articles.*, articles2.* FROM follows
INNER JOIN articles ON (follows.author_id = articles.author_id OR follows.publisher_id = articles.publisher_id)
WHERE follows.user_id = 1
That should work for you.

SELECT articles.* FROM follows,articles
WHERE follows.user_id = 1
AND (follows.author_id = articles.author_id
OR follows.publisher_id = articles2.publisher_id)

Of the top of my head:
SELECT articles.* FROM follows
INNER JOIN articles ON follows.author_id = articles.author_id
WHERE follows.user_id = 1
UNION
SELECT articles.* FROM follows
INNER JOIN articles ON follows.publisher_id = articles.publisher_id
WHERE follows.user_id = 1
order by articles.title

SELECT articles.* FROM follows INNER JOIN articles ON follows.author_id = articles.author_id
union all
SELECT articles2.* FROM follows INNER JOIN articles AS articles2 ON follows.publisher_id = articles2.publisher_id
Something like that.

Related

Return intersection of 3 tables with joins

I have 3 tables : news, users_regions_favorites and users_categories_favorites. Each news has a region and a category.
I want to return all news that are related the a user's favorites (by the regions or categories he likes). And never return a news twice, so no UNION.
Is there a way to do this with join?
You can indeed use UNION:
SELECT n.id
FROM news n
INNER JOIN users_categories_favorites c
ON c.id = n.catid
WHERE c.uid = 1 -- your user here
UNION
SELECT n.id
FROM news n
INNER JOIN users_regions_favorites r
ON r.id = n.regid
WHERE r.uid = 1; -- your user here
UNION will deduplicate, only UNION ALL won't.
But you can also do this using only JOINs:
SELECT DISTINCT n.id
FROM news n
LEFT JOIN users_categories_favorites c
ON c.id = n.catid
LEFT JOIN users_regions_favorites r
ON r.id = n.regid
WHERE c.uid = 1 OR r.uid = 1; -- your user here
See this fiddle showing both results.
you could make (schematically) :
select distinct
news where exists users_regions_favorites
union
news where exists users_categories_favorites

mysql SELECT column only if exists

I have 5 tables I want to select data from, but sometimes the 5th table (named comment) will be empty. When that happens I want my query to return null for the values from that table, and only return the other values (the 5th table includes user comments, sometimes there are none).
Here is my query:
SELECT articles.title, articles.posted, articles.body, authors.name, authors.img, authors.bio, comment.user_id, comment.text, comment.article_id, GROUP_CONCAT(categories.cat_name) AS cat_name
FROM articles, authors, categories, article_categories, comment
WHERE articles.author_id = authors.id
AND articles.id = article_categories.article_id
AND article_categories.category_id = categories.id
AND articles.id = comment.article_id
AND title LIKE :title;
the :title comes from PDO and is not relevant here.
The problem comes from the AND articles.id = comment.article_id. I don't know how to write it in a way to only check for this, if it's there and ignore it otherwise.
Thanks for the help
You should use proper join syntax, and in this case instead of INNER JOIN use LEFT JOIN for comment table:
SELECT articles.title, articles.posted, articles.body, authors.name, authors.img, authors.bio, comment.user_id, comment.text, comment.article_id, GROUP_CONCAT(categories.cat_name) AS cat_name
FROM articles INNER JOIN authors ON articles.author_id = authors.id
INNER JOIN article_categories ON articles.id = article_categories.article_id
INNER JOIN categories ON article_categories.category_id = categories.id
LEFT JOIN comment ON articles.id = comment.article_id
WHERE title LIKE :title;
You should use left/right joins to do that.
[...]
select *
from articles a
left join comments c
on a.id = c.article_id
[...]
To learn more about joins:
http://blog.codinghorror.com/a-visual-explanation-of-sql-joins

MySQL query with multiple INNER JOIN

I'm a little bit confused about a stupid query:
I get rows from the table posts joined with the table authors and the table comments, in a way like this:
SELECT posts.*, authors.name, COUNT(comments.id_post) AS num_comments
FROM posts JOIN authors ON posts.id_author = authors.id_author
LEFT JOIN comments ON posts.id_post = comments.id_post
WHERE posts.active = 1
AND comments.active = 1
this doesn't work, of course.
What I try to do is to retrieve:
1) all my active post (those that were not marked as deleted);
2) the names of their authors;
3) the number of active comments (those that were not marked as deleted) for each post (if there is at least one);
What's the way? I know it's a trivial one, but by now my brain is in offside…
Thanks!
Presumably, id_post uniquely identifies each row in posts. Try this:
SELECT p.*, a.name, COUNT(c.id_post) AS num_comments
FROM posts p JOIN
authors a
ON p.id_author = a.id_author LEFT JOIN
comments c
ON p.id_post = c.id_post
WHERE p.active = 1 AND c.active = 1
GROUP BY p.id_post;
Note that this uses a MySQL extension. In most other databases, you would need to list all the columns in posts plus a.name in the group by clause.
EDIT:
The above is based on your query. If you want all active posts with a count of active comments, just do:
SELECT p.*, a.name, SUM(c.active = 1) AS num_comments
FROM posts p LEFT JOIN
authors a
ON p.id_author = a.id_author LEFT JOIN
comments c
ON p.id_post = c.id_post
WHERE p.active = 1
GROUP BY p.id_post;
Since you are doing a count, you need to have a group by. So you will need to add
Group By posts.*, authors.name
You should you GROUP BY clause together with aggregate functions. Try something similar to:
SELECT posts.*, authors.name, COUNT(comments.id_post) AS num_comments
FROM posts JOIN authors ON posts.id_author = authors.id_author
LEFT JOIN comments ON posts.id_post = comments.id_post
-- group by
GROUP BY posts.*, authors.name
--
WHERE posts.active = 1
AND comments.active = 1
I found the correct solution:
SELECT posts.id_post, authors.name, COUNT(comments.id_post) AS num_comments
FROM posts JOIN authors
ON posts.id_author = authors.id_author
LEFT OUTER JOIN comments
ON (posts.id_post = comments.id_post AND comments.active = 1)
WHERE posts.active = 1
GROUP BY posts.id_post;
Thanks everyone for the help!

Subquery vs join

SELECT
id
FROM
Posts
WHERE
subject_id = 1
OR subject_id IN (
SELECT related_subject_id
FROM RelatedSubjects
WHERE parent_subject_id = 1);
Trying to select all posts for a current subject but also for any sub-subjects which are stored in another lookup table. The above query works, wondering how to accomplish the same thing with a join
SELECT DISTINCT id
FROM Posts AS p
LEFT JOIN RelatedSubjects AS r
ON p.subject_id = r.related_subject_id AND r.parent_subject_id = 1
WHERE p.subject_id = 1 OR r.related_subject_id IS NOT NULL
Assuming proper indexes, UNION often performs better than JOIN with OR:
SELECT p.id
FROM Posts AS p
WHERE p.subject_id = 1
UNION
SELECT p.id
FROM RelatedSubjects AS r
JOIN Posts AS p
ON p.subject_id = r.related_subject_id
WHERE r.parent_subject_id = 1
select
`Posts`.`id`
From posts as `Posts`
LEFT join RelatedSubjects as `RelatedSubjects`
on (`RelatedSubjects`.`related_subject_id` = `Posts`.`subject_id`)
where `Posts`.`subject_id` = 1

MYSQL Relational Query Issue

I'm working with the following DB model
A client has asked me to make a few changes to their DB, I haven't played with relational databases in a few years, generally work with flat DB's
Could someone help me on my way with giving me an example of how the following query would work.
say if I wanted to
select all films with a title like '%Matrix%' under a certain genreID
Any assistance would be greatly appreciated
Use an inner join to join the three tables
SELECT F.title
FROM film F
INNER JOIN filmgenres FG
ON F.filmid = FG.film_filmid
INNER JOIN genres G
ON FG.genres_genreid = G.genreid
WHERE F.title LIKE '%Matrix%' AND G.genre = "Some Genre"
You need to join the tables first,
SELECT a.*, c.Genre
FROM Film a
INNER JOIN FilmGenres b
ON a.FilmID = b.Film_FilmID
INNER JOIN Genres c
ON b.Genre_GenreID
WHERE a.Title LIKE '%matrix%'
To further gain more knowledge about joins, kindly visit the link below:
Visual Representation of SQL Joins
but if you want to search for a specific genre, then you might add a condition as well,
SELECT a.*, c.Genre
FROM Film a
INNER JOIN FilmGenres b
ON a.FilmID = b.Film_FilmID
INNER JOIN Genres c
ON b.Genre_GenreID
WHERE a.Title LIKE '%matrix%' AND c.GenreID = 10
SELECT Film.*
FROM Film f
JOIN FilmGenres fg ON g.Film_FilmID = f.FilmID
WHERE fg.Genres_GenreID = 3
AND Title LIKE '%Matrix%'
SELECT * FROM Film INNER JOIN FilmGenres ON Film.FilmID = FilmGenres.Film_FilmID
WHERE FilmGenres.Genres_GenreID = 1
AND
Film.Title like '%Matrix%'
It's a pretty simple join:
select
Film.*
from
Film
inner join FilmGenres on
Film.FilmID = FilmGenres.Film_FilmID
where
Film.Title like "%Matrix%" and
FilmGenres.Genres_GenreID = ?