Selecting the data that isn't in a query - mysql

I want to select all of the posts that are not marked as read by the user.
tbl_post
post_id post_message
1 hello world
2 good night
3 good morning
4 incredible
5 cool
tbl_mark_as_read
user_id post_id
3 1
3 4
I want to select all data that will not be selected in this query
SELECT p.post_id,p.post_message FROM tbl_post AS p
LEFT JOIN tbl_mark_as_read AS r
ON r.post_id = p.post_id
AND r.user_id = 3
I want the output to be like
post_id post_message
2 good night
3 good morning
5 cool

You could use the not in operator:
SELECT *
FROM tbl_post
WHERE post_in NOT IN (SELECT post_id FROM tbl_mark_as_read);
If you want to exclude only the posts that user 3 has read, you can add a where clause to the inner query:
SELECT *
FROM tbl_post
WHERE post_in NOT IN (SELECT post_id
FROM tbl_mark_as_read
WHERE user_id = 3);

Try this:
SELECT p.post_id,p.post_message FROM tbl_post AS p
LEFT JOIN tbl_mark_as_read AS r
ON r.post_id = p.post_id
AND r.user_id = 3
where r is null

Related

GROUP BY on multiple joins

I have searched for the issue but could not have a working solution.
I have 3 tables: User, Post, Comments.
select u.id user
, p.id post
, c.id comm
from USer u
join Post p
on u.id = p.user_id
join Comments c
on p.id = c.post_id;
This gives me an output with 3 columns that relates a user with the posts they have and the comments received on each.
The o/p is like:
user post comm
1 1 4
1 1 5
1 1 7
1 1 8
2 5 11
2 5 12
2 7 13
I wanted to find the user with the maximum number of overall comm. For this the GROUP BY on user is not working. What could be an alternative for the task?
You can use GROUP BY, ORDER BY, and LIMIT:
select p.user_id, count(*) as num_comments
from Post p inner join
Comments c
on p.id = c.post_id
group by p.user_id
order by num_comments desc
limit 1;
Note that because you only want the user id, you do not need the user table, because user_id is in post.

Get total upvotes for each user for all his posts

The database structure is as follows:
users
[id] [name] [etc...]
1 Echolot ...
2 AnotherUser ...
posts
[id] [user_id] [title] [etc...]
1 1 Post1 ...
2 1 Post2 ...
posts_upvotes
[id] [post_id] [user_id (user who voted)] [value (1 or 0 or -1)]
1 1 1 1
2 1 2 -1
3 2 1 1
4 2 2 1
I'm trying to get a list of all users and their total amount of upvotes sum(value)gathered by all their posts like so:
id: 1
name: Echolot
upvoteCount: 2
id: 2
name: AnotherUser
upvoteCount: 0
I have tried using the following query but without success, I get wrong values and not the right amount of results:
SELECT joinedUsers.id, joinedPosts.postCount, joinedPostsUpvotes.upvoteCount
FROM users joinedUsers
inner join (
SELECT user_id, id, count(id) as postCount
FROM posts
GROUP BY user_id, id
) joinedPosts on joinedPosts.user_id = joinedUsers.id
inner join (
SELECT post_id, sum(value) as upvoteCount
FROM posts_upvotes
GROUP BY post_id
) joinedPostsUpvotes on joinedPostsUpvotes.post_id = joinedPosts.id
GROUP BY joinedUsers.id, joinedPosts.postCount, joinedPostsUpvotes.upvoteCount
Thank you in advance for any hints or solutions.
EDIT: Added test data and expected results.
You don't need all those subqueries, you can just use a couple left join between user, post and post_upvotes:
SELECT t1.id, t1.name, count(distinct t2.id), sum(t3.value)
FROM users t1
LEFT JOIN
posts t2
ON t1.id = t2.user_id
LEFT JOIN
posts_upvotes t3
ON t2.id = t3.post_id
GROUP BY t1.id, t1.name
The use of left join instead of inner join ensures you that users with no posts or posts with no votes will be displayed as well.
I think this would do the trick for you:
SELECT u.id,
u.name,
COUNT(p.id)
SUM(pu.value)
FROM users AS u
JOIN posts AS p
ON u.id = p.user_id
JOIN posts_upvotes AS pu
ON p.id = pu.post_id
GROUP BY u.id,
u.name
If I am not missing something in your question.
I think it would be something like this if you want the count of the amount of times someone voted (not tested)
SELECT users.id, users.name, COUNT(posts_upvotes.id) as upvoteCount
FROM users
INNER JOIN posts_upvotes ON posts_upvotes.user_id = users.id
GROUP BY users.id
Or this if you want the sum of the value of the upvotes:
SELECT users.id, users.name, SUM(posts_upvotes.id) as upvoteSum
FROM users
INNER JOIN posts_upvotes ON posts_upvotes.user_id = users.id
GROUP BY users.id
Or in Laravel you could use the Query Builder: https://laravel.com/docs/5.4/queries
Something like this:
DB::table('users')
->join('posts_upvotes', 'users.id', '=', 'posts_upvotes.user_id'))
->groupBy('users.id')
->select('users.id', 'users.name', DB::raw('COUNT(posts_upvotes) as upvoteCount'))
->get();

MySQL select query from two tables

I have two tables: users and works
I need write select query for count different names from users table where work_status = 1 from works table
The total is: 3 John, 1 Tom
I need get result:
John 2 (2 because one John work_status = 0 ant this not counting)
Tom 1
I have write select that can count different names, just need compared work_status..
SELECT name,COUNT(*) as num FROM users GROUP BY name
My query return:
There is a problem in your question. So here you have two solutions.
If there are three different John working on the company, this is your query
SELECT u.name, COUNT(*) as num
FROM users u INNER JOIN works w ON w.user_id = u.id
WHERE w.work_status = 1
GROUP BY u.name, u.id
If there are only one John working in the company, your query is this one:
SELECT u.name, COUNT(*) as num
FROM users u INNER JOIN works w ON w.user_id = u.id
WHERE w.work_status = 1
GROUP BY u.name
Note: If three John are the same person, you should delete the 2 last and on the works table change user_id = 3 and user_id = 4 for user_id = 1
This is a simple JOIN query:
SELECT u.name, COUNT(*) num
FROM users u
JOIN works w
ON w.user_id = u.id
AND w.work_status = 1
GROUP BY u.name
This one should do the job:
SELECT users.name,SUM(works.work_status) as num
FROM users,works
WHERE users.id=works.id
GROUP BY name
SELECT
users.`name`,
COUNT(*) num
FROM
users,
works
WHERE users.`id` = works.`user_id`
AND works.`work_status` = 1
GROUP BY users.`name` ;

mysql Select rows with exclusive AND

I have a table of posts and a table of post_tags
Here is my post table structure example:
post_id int(11)
post_user int(11)
post_title text
post_content longtext
and this is my post_tags structure example :
post_id int(11)
tag_id int(11)
What I need is selecting all posts from posts table that have tag_id of 1 AND 2 at the same time, I've tried different joins without success.
example of post_tags table data :
post_id tag_id
1 1
2 1
5 2
6 1
6 2
HERE for example my query should return post (from post table) whos id is 6, watch in the example post_id of 6 has tag_id 1 AND tag_id 2 NOT ONLY ONE OF THEM but BOTH at the same time.
You can do this with aggregation:
select post_id
from post_tags pt
group by post_id
having sum(tag_id = 1) > 0 and
sum(tag_id = 2) > 0;
If you want to see the actual information from posts, just join that table in.
EDIT (a bit of an explanation):
You have a "set-within-sets" query. This is a common query and I prefer to solve it using aggregation and a having clause, because this is the most general approach.
Each condition in the having clause is counting the number of rows that match one of the tags. That is, sum(tag_id = 1) is counting up the rows in post_tags where this is true. The condition > 0 is just saying "tag_id = 1 exists on at least one row".
The reason I like this approach is because you can generalize it easily. If you want tags 3 and 4 as well:
having sum(tag_id = 1) > 0 and
sum(tag_id = 2) > 0 and
sum(tag_id = 3) > 0 and
sum(tag_id = 4) > 0;
And so on.
select a.post_id, b.post_id, a.post_user, b.post_tags
from posts as a
inner join post_tags as b
where b.post_tags in(1, 2)
or
select a.post_id, b.post_id, a.post_user, b.post_tags
from posts as a
inner join post_tags as b
where b.post_tags =1 or b.post_tags = 2
This should work
select q.* from (
select p.post_id as post_id from post_tags p
where p.tag_id=1
and exists (
select 1 from post_tags p2
where p2.post_id=p.post_id
and p2.tag_id=2)
) as t
inner join posts q on posts_id=t.post_id;
Give this a try:
SELECT post.*
FROM (SELECT T1.post_id
FROM (SELECT * FROM post_tags WHERE 1 IN(tag_id)) T1
INNER JOIN (SELECT * FROM post_tags WHERE 2 IN(tag_id)) T2 ON T1.post_id = T2.post_id)
T3
INNER JOIN post ON T3.post_id=post.post_id;
SQL Fiddle Link: http://sqlfiddle.com/#!2/04f74/33

MySQL JOIN with WHERE clause, not returning all results

I'm working on PHP/MySQL based 'Blog Post' System, the posts can have multiple Categories, I have 2 tables:
posts:
PostId Content
0 POST0
1 POST1
2 POST2
3 POST3
post_categories:
PostId CategoryId
0 1
0 2
0 3
1 2
1 4
2 3
3 1
(I omitted some columns)
I also have a 3rd table 'categories', that describes the categories, but that's not relevant.
Giving a CategoryId (eg: 2), I want to return all posts containing that CategoryId, on the following form:
PostId Contents CategoriesIds
0 POST0 1, 2, 3
1 POST1 2, 4
(Both post 0 and post 1 must be returned, they both have the CategoryId 2)
The problem is that using this query:
SELECT p.PostId, p.Content, GROUP_CONCAT(pc.CategoryId SEPARATOR ',') AS CategoriesIds
FROM posts AS p
LEFT JOIN post_categories AS pc ON p.PostId=pc.PostId
WHERE pc.CategoryId = 2 GROUP BY p.PostId
both posts are returned but NOT all Category Ids,
PostId Content CategoriesIds
0 POST0 2
1 POST1 2
I want to return ALL posts with CategoryId 2 but still returning ALL CategoriesIds for those posts.
Is it possible to do so?
Thanks
Looks like you need to separate out the selection of the posts with categoryId = 2 from the selection of the fields - try something like this:
SELECT p.PostId, p.Content,
GROUP_CONCAT(pc.CategoryId SEPARATOR ',') AS CategoriesIds
FROM posts AS p
LEFT JOIN post_categories AS pc ON p.PostId=pc.PostId
where p.PostId in
( select PostId from post_categories where CategoryId = 2)
GROUP BY p.PostId
The simpliest solution without using IN clause would be
SELECT a.postID, c.content,
GROUP_CONCAT(b.categoryID ORDER BY b.categoryID ) CategoryList
FROM
(
SELECT PostID
FROM post_categories b
WHERE CategoryID = 2
) a
INNER JOIN post_categories b
ON a.postID = b.postID
INNER JOIN post c
ON c.PostID = b.PostID
GROUP BY a.postID, c.content
SQLFiddle Demo