Counting votes and if user voted in MySQL table - mysql

I have 3 MySQL tables:
Features table:
id name
----------
1 feature 1
2 feature 2
3 feature 3
4 feature 4
5 feature 5
Votes table:
userid featureid
----------------
1 1
1 2
1 3
1 4
2 1
2 2
2 3
2 4
3 1
3 2
3 3
4 1
Users table:
id name
--------
1 John
2 Alice
3 Bob
4 Mark
5 Jane
6 Mary
7 Ann
I need to fetch in single query:
always all features, regardless if they have votes or not
each feature must be listed only once regardless of number of votes even if it has no votes
votes count for each feature listed
a special mark if currently logged user voted for current feature in the list - for example if user 3 is logged in, then list all features with votes count for all users and if user 3 voted for some features have a special field indicating his vote or NULL if he didn't (other data from votes table must be included too so it needs to be LEFT JOINED)
So far I did this:
SELECT *,(SELECT COUNT(*) FROM votes WHERE votes.featureid=features.id) AS "votecnt"
FROM features
LEFT JOIN votes ON votes.featureid=features.id
LEFT JOIN users ON users.id=votes.userid
GROUP BY features.id
It lists all features but has no special field if user 3 voted. I tried with IF, various WHERE conditions and after a lot of tries... ran out of ideas.
Desired output might look like this:
features.id features.name votes.userid votes.otherfields users.id users.name
1 feature 1 4 - 4 Mark
2 feature 2 NULL - NULL NULL
3 feature 3 NULL - NULL NULL
4 feature 4 NULL - NULL NULL
5 feature 5 NULL - NULL NULL
All the features are listed and only those where user 4 voted have other joined tables filled, others are simply NULL. If someone else voted for feature 2 it is still NULL as it is of no relevance for user 4 because in this example user 4 is logged in.
Here is the problem:
http://sqlfiddle.com/#!2/c3d10/3
http://sqlfiddle.com/#!2/c3d10/4
http://sqlfiddle.com/#!2/c3d10/5
http://sqlfiddle.com/#!2/c3d10/6
http://sqlfiddle.com/#!2/c3d10/7
All of above queries in SQLFiddle it should output all 5 features regardless of the userid. So the query must be modified somehow to show all features - even if other people voted for a feature or if there are no votes or if current user voted for feature.

this should do it:
SELECT tmp1.id, name, votecnt, user_id, user_name FROM
(SELECT *,(SELECT COUNT(*) FROM votes WHERE votes.featureid=features.id) AS "votecnt" FROM features) as tmp1
LEFT JOIN (SELECT features.id as feature_id, users.id as user_id, users.name as user_name FROM features
JOIN votes ON votes.featureid=features.id
JOIN users ON users.id=votes.userid
WHERE users.id=3) as tmp2 on tmp1.id = tmp2.feature_id
probably it's not the prettiest sql, and most likely there's also room for optimization

I think you're asking to have all features returned, all counts returned, but only user information if they voted for that feature.
I get the results you're looking for if you specify the userid
select f.id
, f.name
, u.id
, u.name
, v.votecnt
from features f
left join (select featureid, COUNT(userid) votecnt from votes group by featureid) v on v.featureid = f.id
left join votes v1 on v1.featureid = f.id and v1.userid = 4
left join users u on u.id = v1.userid
I chose to specify the userid inside the left join to the votes. Anywhere else and it limits the total number of rows returned.
Results:
1 feature 1 4 Mark 4
2 feature 2 NULL NULL 3
3 feature 3 NULL NULL 3
4 feature 4 NULL NULL 2
5 feature 5 NULL NULL NULL
Example with "Bob"
select f.id
, f.name
, u.id
, u.name
, v.votecnt
from features f
left join (select featureid, COUNT(userid) votecnt from votes group by featureid)v on v.featureid = f.id
left join votes v1 on v1.featureid = f.id and v1.userid = 3
left join users u on u.id = v1.userid
Results:
1 feature 1 3 Bob 4
2 feature 2 3 Bob 3
3 feature 3 3 Bob 3
4 feature 4 NULL NULL 2
5 feature 5 NULL NULL NULL

Related

How to find the dependency that brought another dependency in MySQL or Redshift

There is two tables:
dependency_permission table:
id
dependency_permission_id
2
1
4
2
user_permission table:
id
user_id
permission_id
1
11111
1
2
22222
4
3
22222
2
4
11111
2
5
33333
2
I want to write a query that finds all user_id's which have permissions depend on another permission the user doesn't have.
from the above data, users (22222 and 33333) should be returned, they don't have permission 1 which 2 depends on.
You may left join the 'user_permission' table with the 'dependency_permission' to get the 'dependency_permission_id' for each user 'permission_id', then use NOT EXISTS operator to check the existence of 'dependency_permission_id' for each user 'permission_id'.
with user_dependency_permission as
(
select U.user_id, U.permission_id, D.dependency_permission_id
from user_permission U left join dependency_permission D
on U.permission_id = D.id
)
select user_id, permission_id /* if you want to select only user_ids use distinct user_id*/
from user_dependency_permission T
where not exists(
select 1 from user_dependency_permission D
where T.user_id=D.user_id and
D.permission_id=T.dependency_permission_id
)
and T.dependency_permission_id is not null
See a demo on MySQL.

MySQL join not producing expected results with 'in' function

I have a game_players table like this (other columns omitted for brevity):
game_id user_id
1 1
1 3
2 1
2 2
2 4
My intention is to show the user that's logged in only the games they're involved in (e.g. user 2 should only see game 2).
The "where 2 in(select game_players.user_id from game_players)" bit doesn't appear to be working, I get a list of all the games - including the ones user 2 isn't involved in.
select games.game_id as 'game_id',
games.date_game_started as 'date_started',
users.username as 'username',
users.permanent_id as 'permanent_id',
game_players.user_id as 'user_id'
from games
inner join game_players on games.game_id = game_players.game_id
inner join users on game_players.user_id = users.user_id
where 2 in(select game_players.user_id from game_players)
and games.game_active = 1
and game_players.current_turn = 1
group by(games.game_id)
order by field(game_players.user_id, 2) desc,
games.date_game_started asc
Given my test data, I get this result set:
game_id date_started username permanent_id user_id
1 2021-12-15 13:33:17 userc userc 3
2 2021-12-15 13:35:20 Admin admin 1
I should only be getting the second row, because user 2 is only involved in game 2.
I'll admit that my SQL is a bit rusty, please can you help?
I simplified/broke it down and found the answer. I think... so far so good on my testing of it.
Needed to change:
where 2 in(select game_players.user_id from game_players)
to...
where game_players.game_id in(select game_id from game_players where user_id = 2)

Two tables with all rows including allocated based on id

Im trying to make this generic as it might help others in the future.
For an example i have two tables one with books and the other is the user with which book they have read, So ide like to display all the books and include a temporary column value as a (yes / no or 0/1), i have tried a join but the ( WHERE user_id = 3) clause only then return the one row and not all the other rows.
book.book_id book.book_name
10 Book 1
11 Book 2
12 Book 3
-------------
user.user_id user.book_id
1 10
1 12
2 11
3 12
Desired output:
user_id book_id temp_col_read
3 10 0 // yes, on or null
3 12 1 // or yes
3 13 0
This is actually quite simple. In the event that a user could read a book multiple times, I would go with exists in the select:
select b.*,
(case when exists (select 1
from reads r
where r.book_id = b.book_id and r.user_id = 3
)
then 1 else 0
end) as user_read_book
from book b;
In MySQL, the case is not strictly necessary because a boolean expression is treated as 0/1 in many contexts:
select b.*,
(exists (select 1
from reads r
where r.book_id = b.book_id and r.user_id = 3
) as user_read_book
from book b;
You can use a left join and where the join is unresolved then is not read
select
user.user_id
, book.book_id
, case
when book.book_id is null
then 'NO' else 'YES'
end as temp_col_read
from book
left join user on user.book_id = book.book_id

Calculate round(avg) from same table and join

I have two tables,
users
userid fname usertype
1 Steve vendor
2 Peter vendor
3 John normaluser
4 Mark normaluser
5 Kevin vendor
6 Alan vendor
7 Sean vendor
vendor_rating
id userid rating
1 1 4
2 2 3
3 2 2
4 2 4
5 1 3
6 5 2
7 5 2
userid is foreign key.
i want to show all vendors (only usertype vendor) from user table by descending/ascending average rating even if Vendor's rating is not available on table it should show, its information should display at last in descending, at first in ascending.
I want to fetch all users info from first table so i m using left join :
SELECT
users.name,
users.userid,
users.usertype
FROM users
LEFT JOIN (SELECT
ROUND(AVG(rating)) AS rating_avg,
userid
FROM vendor_rating
ORDER BY rating_avg DESC) ven
ON users.usertype = 'vendor'
AND users.userid = ven.userid
ORDER BY ven.rating_avg ASC;
Please help where am i going wrong.
EDIT:
I get this
userid ROUND(AVG(vr.ratings))
28 5
27 4
16 3
26 2
25 0
NULL NULL
NULL NULL
NULL NULL
NULL NULL
if i use
SELECT vr.userid, ROUND(AVG(vr.ratings)) FROM vendor_rating vr
RIGHT JOIN (SELECT users.fname, users.userid, users.usertype FROM users) u
ON u.id = vr.vendor_id WHERE u.usertype = 'vendor' GROUP BY vr.userid,u.fname
ORDER BY round(avg(vr.ratings)) ASC
i get NULL values from users table whose rating is not available in vendor_rating table those should display userids
Try to this
SELECT
vr.userid,
u.fname,
ROUND(AVG(vr.rating))
FROM vendor_rating vr
INNER JOIN users u
ON u.userid = vr.userid
WHERE u.usertype = 'vendor'
GROUP BY vr.userid,
u.fname
ORDER BY round(avg(vr.rating)) ASC
finally i got it
SELECT users.fname, users.userid,users.usertype
FROM users
LEFT JOIN (select ROUND(AVG (ratings)) AS rating_avg,userid FROM
vendor_rating group by userid order by rating_avg desc ) ven
ON users.id=userid
WHERE users.usertype='vendor'
order by rating_avg desc
Thank you all, for sharing views to get idea to solve my problem.

MySQL three table join, find item's rating

I'm hoping this is an easy one for you gurus, but my SQL knowledge is failing me.
My sample dataset:
item
----
item_id item_name item_added
1 Apple <date_time>
2 Banana <date_time>
user
----
user_id user_name
1 Alice
2 Bob
3 Carol
rating
------
rating_id item_id user_id rating_value
1 1 1 3
2 1 2 4
3 1 3 5
4 2 1 5
5 2 2 2
I want to find out what rating all three users have given to a particular item. The output should include NULL for rating_value if the user hasn't rated the item. For example, if I have item_id 2, I'd like to get this output:
user_name item_name rating_value
Alice Banana 5
Bob Banana 2
Carol Banana NULL
I've tried all kinds of joins, but I just can't seem to figure this one out.
Many thanks in advance.
It looks like you want a cartesian product of user and item, which will then be joined with rating:
select user_name, item_name, rating_value
from user as u, item as i
left join rating as r
on r.user_id = u.user_id
and r.item_id = i.item_id
I haven't done any serious work with MySQL for 4.5 years, but this should do it.
Edit: Maybe MySQL requires AS for the table aliases.
select u.user_name, i.item_name, r.rating_value
from item i,user u
left join rating r
on r.user_id = u.user_id
and r.item_id = i.item_id
This should do the trick..