Group by grouped column? - mysql

I have a table of node likes, which looks roughly like this:
lid nid uid type
1 23 3 like
2 23 1 like
3 49 3 dislike
4 11 6 like
lid = unique ID for this table, nid = "node" (content) ID, uid = user ID and type is self explanatory.
With this query:
SELECT nid, COUNT(lid) AS score, type
FROM node_likes
INNER JOIN users ON node_likes.uid = users.uid
GROUP BY nid, type
I can get each node with its like and dislike scores. The inner join is irrelevant; some (dis)likes are from users that no longer exist, and the join is to eliminate them.
The result looks like this:
nid score type
307 4 like
307 1 dislike
404 24 like
How can I then sub-group this query by type, and return the top-scoring node ID for each "like" type (like/dislike)?
Ie.
nid score type
404 24 like
307 1 dislike

SELECT
SUBSTRING_INDEX(GROUP_CONCAT(nid ORDER BY likes DESC),',',1) as most_likes_nid,
MAX(likes) as most_likes,
SUBSTRING_INDEX(GROUP_CONCAT(nid ORDER BY dislikes DESC),',',1) as most_dislikes_nid,
MAX(dislikes) as most_dislikes
FROM (
SELECT
nid,
COUNT(IF(type = 'like', 1, null)) as likes,
COUNT(IF(type = 'dislike', 1 ,null)) as dislikes
FROM node_likes
GROUP BY nid
) as t

SELECT nid, COUNT(lid) AS score, type
FROM node_likes
INNER JOIN users ON node_likes.uid = users.uid
GROUP BY nid, type
ORDER BY type DESC, score DESC;
may do the trick.

Try this:
SELECT
nid, max(score) as score, type
FROM (
SELECT nid, COUNT(lid) AS score, type
FROM node_likes
INNER JOIN users ON node_likes.uid = users.uid
GROUP BY nid, type
) results
GROUP BY type
ORDER BY type DESC, score DESC

Related

Avoid using a subquery in a table join

In a MySQL 5.7 database, I have the following User table:
Name
Id
David
1
Frank
2
And the following Order table:
Id
Price
UserId
1
55
1
2
68
1
3
50
1
4
10
2
For every user, I want to select the price of the order with the biggest ID.
I can use the following query which adds additional complexity due to the nested subquery :
SELECT
User.Name,
last_user_order.Price
FROM User
LEFT JOIN (
SELECT Price, UserId FROM Order
ORDER BY Id DESC LIMIT 1
) AS last_user_order ON last_user_order.UserId = User.Id
There exist many questions here where the column to be selected is the same than the one being ordered. Hence, it is possible to use MAX in the first SELECT statement to avoid a subquery. Is it possible to avoid a subquery in my case?
For every user, I want to select the price of the order with the biggest ID.
That looks like:
SELECT
u.*,
o.Price,
FROM
User u
INNER JOIN Order o ON u.ID = o.UserID
INNER JOIN
(
SELECT MAX(ID) as OrderID FROM Order GROUP BY UserId
) maxO ON o.Id = maxO.OrderId
SELECT User.Name,
( SELECT Order.Price
FROM Order
WHERE Order.UserId = User.Id
ORDER BY Order.Id DESC LIMIT 1 ) LastPrice
FROM User;

SQL getting values and then total count of those values

I am working on an auction script with a table that looks like the attached image.
Within an auction two users can place the same bid amount, for example two players could place a bid of '1' which would give that bid amount a total count of two.
I need a query which gets all of a users single bid amount along with the total count of that bid amount within the scope of the auction id.
As it stands I first get the users bids:
SELECT bid_amount FROM all_bids WHERE auction_id = '129' AND user_id = '9'
And then I am looping through each amount in PHP and doing the following
SELECT COUNT(*) FROM all_bids WHERE auction_id = '129' AND bid_amount = 'xxx'
But this is of course very power hungry and I am sure it can be done all in one query.
I have got as far as
SELECT bid_amount,COUNT(*) FROM (SELECT bid_amount FROM all_bids WHERE auction_id = '129' AND user_id ='9') as foo GROUP BY bid_amount
Which returned the correct bid amounts but the counts were all 1 which is wrong as I guess my incorrect query is only counting the values within the subquery and not outside of the bids that just that user placed
SELECT a.*, b.cnt from all_bids a
join (select bid_amount,COUNT(*) cnt from all_bids group by bid_amount) b
on a.bid_amount=b.bid_amount
WHERE a.auction_id = '123' AND a.player_id = '456'
I think you want group by:
select bid_amount, count(*)
from all_bids
where auction_id = 123 and player_id = 456
group by bid_amount;
Note that you do not need single quotes for a numeric constant.
Hmmm. You might want this phrased as:
select ab.bid_amount, count(*)
from all_bids ab
where ab.auction_id = 123 and
ab.bid_amount in (select ab2.bid_amount
from all_bids ab2
where ab2.auction_id = ab.auction_id and ab2.player_id = 456
)
group by ab.bid_amount;

sql join query to get most viewed atricles by country

having a hard time pulling articles based on user country views
i have the following tables with their fields
user: id, country_id
article: id
article_views: id, user_id, article_id
each time a user views an article I insert it into the article_views table, like this:
article_views.id article_id user_id
2 1 1
3 2 1
4 2 2
5 2 2
I want to pull the highest viewed article for current user.country_id. I imagine it will contain:
order by article_views.article_id DESC
Any suggestions?
This should work fine:
select article_id, count(*) as views
from article_views inner join user on article_views.user_id = user.id
group by user.country_id
order by views desc;
Edit. I forgot about grouping article_id as #Josien pointed, the correct query is:
select country_id, article_id, count(*) as views
from article_views inner join user on article_views.user_id = user.id
where country_id = ':countryID'
group by user.country_id, article_id
order by views desc;
select av.id, count(*) as views
from article_views av inner join user u on av.user_id = u.id
where u.country_id = 'cc'
group by u.country_id
order by views desc;

SQL syntax to retrieve votes strangely misses some results

I've tried to look other posts to do a Mysql query, I think I'm almost there but for some reason I have a bug.
I have those tables :
POSTS [id_post (int) / activity (int) ]
VOTES [id_vote (int) / id_post (int) / user_id (int)]
ACTIVITIES [id_activity (int) / activity_name (varchar)]
I have in fact more tables and fields than that, but these are the relevant one for my problem. I created a vote system that adds a user vote to the VOTES table, refering to the post that is being voted and the user account of the voting person.
So the votes table may look like this :
id_vote | id_post | user_id
1 5 8
2 6 8
Every post belongs to an activity. I would like to lists the posts which have the most votes for each activity.
That's where I am so far :
SELECT activities.id_activity, activities.activity_name, votes.id_post, votes.totalvotes AS allvotes
FROM ( SELECT votes.id_post, COUNT(*) as totalvotes
FROM `votes`
GROUP BY votes.id_post
) AS votes
JOIN posts ON posts.id_post = votes.id_post
JOIN activities ON posts.activity = activities.id_activity
GROUP BY activities.id_activity
HAVING allvotes = MAX(totalvotes)
This works well, I retrieve what I want except that if 2 posts in the same activity have the same amount of votes, I have no idea which one only appears after grouping those posts, and why it's not the other one.
id_activity | activity_name | id_post | allvotes
5 eating 5 2
3 sleeping 6 1
More importantly, what is really bugging me is that some activities won't show up for some reason. I noticed that, in the above example, if the post 5 belonging to the eating category is the one that has the most votes indeed, then the category appears. BUT if it happens that the post for the eating category which have the mosts votes IS NOT post 5 (which is the one MYSQL decided to show up by default), the the whole row about the eating category just WON'T SHOW UP at all.
It's been 2 days I'm on this...
Any ideas ?
Thanks a bunch.
It is little complex to implement in a single query. it will be easy with some temporary tables. if you want it in a single query then you can try with row_number.
example:
SELECT id_activity,activity_name,id_post,v_count FROM(
SELECT id_activity,activity_name,id_post,v_count
,#rownum := IF(#prev_value=id_activity,#rownum+1,1) AS RowNumber
,#prev_value := id_activity
FROM (
select a.id_activity,a.activity_name,p.id_post,v.v_count
from activities a
left join posts p on p.activity=a.id_activity
left join (
select id_post,count(*) v_count from votes v1
group by id_post
) v on v.id_post=p.id_post
order by a.id_activity, v.v_count desc) as tmp
,(SELECT #rownum := 0) r
,(SELECT #prev_value := '') y
)tmp2
WHERE rownumber=1
FIDDLE
i guess its because you group by the activity, i wonder why this statement is allowed in mysql, in mssql you are not allowed to group only by activities.id_activity in this case, you had to group by activity and/or id_post
try this:
SELECT activities.id_activity, activities.activity_name, votes.id_post, COUNT(*) as totalvotes
FROM votes
INNER JOIN posts ON posts.id_post = votes.id_post
INNER JOIN activities ON posts.activity = activities.id_activity
GROUP BY votes.id_post
HAVING totalvotes = MAX(totalvotes)
and lmk if it works :)
or your statement modified:
SELECT activities.id_activity, activities.activity_name, votes.id_post, votes.totalvotes AS allvotes
FROM ( SELECT votes.id_post, COUNT(*) as totalvotes
FROM `votes`
GROUP BY votes.id_post
) AS votes
JOIN posts ON posts.id_post = votes.id_post
JOIN activities ON posts.activity = activities.id_activity
GROUP BY votes.id_post
HAVING allvotes = MAX(totalvotes)
if i get you right then its because you have 2 posts with the same activity, as soon as you group the activities and not group the posts, one post disappears.

MySQL Join 3 Tables on Single Resource ID

I have three tables
resources_connection
resource_id
resource_tag_id
resources_flags
user_id
resource_id
resources_votes
user_id
resource_id
Each is a 2 column table, int (11) for both designed to allow me to query the number of tags, flags and votes based on a single 'resource id'
I am currently using this query to try and get a count of tags (resources_connection), flags(resources_flags) and votes(resources_votes):
SELECT COUNT(DISTINCT t1.resource_id) as votes,
COUNT(DISTINCT t2.resource_id) as flags,
COUNT(DISTINCT t3.resource_tag_id) as tags
FROM ecruit_demo.resources_votes t1
LEFT JOIN ecruit_demo.resources_flags t2
ON (t1.resource_id = t2.resource_id)
JOIN ecruit_demo.resources_connection t3
ON (t1.resource_id = t3.resource_id) WHERE t1.resource_id = 4
The problem is that this query returns proper results for resource_id = 1 but when I set resource_id to 4 (for which there is a tag) it returns all zeros. What would be the proper query structure to ensure that this query always returns the proper count of tags, flags and votes for a given resource_id?
I should also add that the only place resource_id = 4 occurs in the database is in resources_connection, the other two tables do not have this value
SELECT resource_ID,
MAX(CASE WHEN types = 'votes' THEN totals ELSE NULL END) votes,
MAX(CASE WHEN types = 'flags' THEN totals ELSE NULL END) flags,
MAX(CASE WHEN types = 'tags' THEN totals ELSE NULL END) tags
FROM
(
SELECT resource_ID, 'votes' types,
COUNT(DISTINCT resource_ID) totals
FROM resources_votes
GROUP BY resource_ID
UNION
SELECT resource_ID, 'flags' types,
COUNT(DISTINCT resource_ID) totals
FROM resources_flags
GROUP BY resource_ID
UNION
SELECT resource_ID, 'tags' types,
COUNT(DISTINCT resource_tag_id) totals
FROM resources_connection
GROUP BY resource_ID
) s
-- WHERE resource_ID = 1
GROUP BY resource_ID
I'm not really good in mysql but maybe you can try:
change JOIN to LEFT JOIN so the query would be like this.
SELECT COUNT(DISTINCT t1.resource_id) as votes,
COUNT(DISTINCT t2.resource_id) as flags,
COUNT(DISTINCT t3.resource_tag_id) as tags
FROM ecruit_demo.resources_votes t1
LEFT JOIN ecruit_demo.resources_flags t2
ON (t1.resource_id = t2.resource_id)
LEFT JOIN ecruit_demo.resources_connection t3
ON (t1.resource_id = t3.resource_id) WHERE t1.resource_id = 4