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
Related
I have two tables: Authors and Posts. Authors can have several Posts (1:N).
Authors:
- id
- name
Posts:
- id
- authors_id
- post_type
I need to retrieve all Authors where their post_type is not of a certain type, lets say "Type1", which would need by logic to include Authors without posts at all.
I currently try this:
Select DISTINCT a.*
from authors a
LEFT JOIN posts p ON p.authors_id = a.id
WHERE
p.post_type <> 'TYPE1'
OR p.authors_id IS NULL
This will return correctly all authors which have:
- no posts at all
- or have only posts not of TYPE1
- and will not show authors having only posts of TYPE1
BUT: It will return also those authors who have TYPE1 plus any other type of post.
Is this possible in a query?
I think this ought to work:
Select DISTINCT a.*
from authors a
LEFT JOIN posts p ON p.authors_id = a.id
WHERE
a.id NOT IN (SELECT p.authors_id from authors a INNER JOIN posts p ON p.authors_id = a.id WHERE p.post_type = 'TYPE1')
You can change you query a bit by getting the grouping first and then joining to it
Select DISTINCT a.*
from authors a
LEFT JOIN
(
select authors_id, group_concat(post_type order by post_type) as post_list
from posts
group by authors_id
having post_list not like 'TYPE1%'
) tab ON tab.authors_id = a.id
WHERE tab.authors_id IS NULL
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!
I use this query to select all articles :
SELECT articles.*,categories.category_name,users.username,tags.tag
FROM articles
LEFT JOIN `categories` ON articles.category_id = categories.category_id
LEFT JOIN `users` ON articles.author_id = users.user_id
LEFT JOIN `tags` ON articles.article_id = tags.article_id
ORDER BY articles.date_added DESC
I have an other table comments, and I want to count how many comments are there, where the article_id in that table = article_id in the articles table. I tried with COUNT, but then it returns only one result. How can I do that with one query?
You can use a subquery in the SELECT clause:
SELECT articles.*,categories.category_name,users.username,tags.tag, (SELECT count(*) FROM comments c WHERE c.article_id = articles.article_id) as comments_count
As arnaud576875 already stated, you can use a subquery to extract the summary data.
Two things I've noticed from your SQL that are not really a part of the question but still worth pointing out.
you can use a table alias to shorten your SQL and make it more readable.
So instead of
SELECT articles.*,categories.category_name,users.username,tags.tag
FROM articles
LEFT JOIN `categories` ON articles.category_id = categories.category_id
LEFT JOIN `users` ON articles.author_id = users.user_id
LEFT JOIN `tags` ON articles.article_id = tags.article_id
ORDER BY articles.date_added DESC
you'd code
SELECT a.*, c.category_name, u.username, t.tag
FROM articles a
LEFT JOIN `categories` c ON a.category_id = c.category_id
LEFT JOIN `users` u ON a.author_id = u.user_id
LEFT JOIN `tags` t ON a.article_id = t.article_id
ORDER BY a.date_added DESC
I would drop SELECT * and select only the fields that you actually are going to use. This also helps with readability of your code.
I have models(tables) in my database with table and fields name like
tags (id, name)
taggings (id, tag_id, taggable_id, taggable_type, context)
employment_histories (id, user_id, grades, subjects, my_interests )
users (id)
taggable_id is actually employment_histories_id and context can either be grade or subjects or my_interests
now I have array of tags e.g. g={"9th","10th"}
and I want to get users, only whose tags are all matching to the above g array.
I've written the query below:
SELECT DISTINCT users.* FROM `users`
LEFT OUTER JOIN `employment_histories`
ON `employment_histories`.`user_id` = `users`.`id`
LEFT OUTER JOIN `taggings`
ON `employment_histories`.`id` = `taggings`.`taggable_id`
AND `taggings`.`taggable_type` = 'EmploymentHistory'
LEFT OUTER JOIN `tags` ON taggings.context = 'subjects'
WHERE tags.name='9th' OR tags.name='10th'
but it gives me those users too, which match any of the tags, however I want that it will return only that user who match all the two tags
Suppose that tags 9th and 10th have tag id 9 and 10 then what i want that it will only return the taggable_id(which is employmenthistories.id) who has common taggable_id for these two tag_id (that is 9 and 10) in taggings table
for example i have two user tariq and kamal and both of these users have 9th tag common but kamal dont have tag 10th so want query which if passed these two tags should return only tariq whose tags are all macthing these two tags but users like kamal which match any of the tags should be filtered too
From php chat room:
SELECT
users.* ,
count(*) AS count
FROM users
LEFT JOIN employment_histories ON users.id = employment_histories.user_id
LEFT JOIN tagging ON tagging.taggable_id = employment_histories.id
LEFT JOIN tags ON tags.id = tagging.tag_id
WHERE tags.name = "9th"
OR tags.name = "10th"
GROUP BY users.id
HAVING count = 2
SELECT users.* FROM users
INNER JOIN employment_histories
ON employment_histories.user_id = users.id
INNER JOIN taggings
ON employment_histories.id = taggings.taggable_id
AND taggings.taggable_type = 'EmploymentHistory'
AND taggings.context = 'subjects'
INNER JOIN tags ON tags.id = taggings.tag_id
WHERE tags.name IN ('9th','10th')
GROUP BY users.id
HAVING COUNT(DISTINCT(tags.name)) = 2;
I have re-wrote the query.
Few changes:
Joining Tags on tags.id = taggings.tag_id
Remove OR from where clause, and use in, improves the performance.
SELECT DISTINCT users.*, count(*) as totRow FROM `users`
LEFT OUTER JOIN `employment_histories`
ON `employment_histories`.`user_id` =
`users`.`id` LEFT OUTER JOIN
`taggings` ON
`employment_histories`.`id` =
`taggings`.`taggable_id` AND
`taggings`.`taggable_type` =
'EmploymentHistory' AND
`taggings`.`context` = 'subjects'
LEFT OUTER JOIN `tags` ON `tags`.`id` = `taggings`.`tag_id`
WHERE tags.name = '9th' or tags.name = '10th'
GROUP BY `users`.`id`
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.