Get top 5 most popular values in mysql with where clause - mysql

I'm working on a project and I have a problem. I have a table namedfriendswith three columnid,from_emailandto_email(it's a social networking site and "from_email" is the person that follows the "to_email"). I want a query to return the top 5 friends I follow according to the number of their followers. I know that the query for top 5 is:
SELECT
to_mail,
COUNT(*) AS friendsnumber
FROM
friends
GROUP BY
to_email
ORDER BY
friendsnumber DESC
LIMIT 5
Any ideas?
I would also like to return friends with the same number of followers ordered by their name. Is it possible?

You should use COUNT(from_email) instead of COUNT(*); because you want to calculate the number of followers, which is represented by from_email.
Thus, your select clause would be something like:
SELECT to_email, COUNT(from_email) as magnitude
as for getting the most popular people that you follow, you could use IN clause:
WHERE to_email IN (SELECT to_email FROM friends WHERE from_email='MY_EMAIL');
and about name, you shall join this query with the other table which contains the name value.
Since you've got the essentials now, I hope you can try to compose the full query on your own =)

Join again to the table for the 2nd tier count:
SELECT f1.to_email
FROM friends f1
JOIN friends f2 on f2.to_mail = f1.to_email
WHERE f1.from_email = 'myemail'
GROUP BY 1
ORDER BY count(*) DESC
LIMIT 5
If an index is defined on to_email, this will perform very well.

Related

Order by subquery MySQL

Hello I want to display the name of the activities according to the order of their addition in the feed (FeedId=incremented id) and with a preselected team number however the order in the subquery is not respected.
MariaDB [database]
select name from activity where id=(select ActivityId from feed where TeamId=16 order by FeedId);
I tried to take out the order by but without success. Someone would have any idea ?
Try using inner join, something like this
select name
from activity
inner join feed on feed.ActivityId = activity.id
where TeamId=16
order by FeedId;

JOIN, ORDER BY and GROUP BY in same statement

I have two tables - results and contestants. Result table cointains
result_id (PK)
resul_contestant (who scored this)
value (how much did he scored)
result_discipline(where he scored this)
contestants table cointains
contestant_id (PK)
contestant_name
contestant_category
What I want to do is to select results for all contestants, but I only want to show one result per contestant - the highest (lowest) one.
So far I managed to do this:
SELECT * FROM contenstants
JOIN results ON result_contestant = contenstant_id
WHERE result_discipline = 1 AND contestant_category = 1
ORDER BY value DESC
GROUP BY contenstant_id;
However, this gives me syntax error. If I delete the GROUP BY line, I got results ordered from highest, but if any of the contestants scored in this discipline more than once, I got all of his scores.
If I delete the ORDER BY line, I got only one result per contestant, but it returns the first record in db, not the highest one.
How to fix this command to be valid? Also, there are some less_is_better disciplines, where I want the lowest score, but as far as I could use the ORDER BY on final query, it should be achieved by replacing DESC with ASC.
Thanks.
Don't use group by. Using select * with group by just doesn't make sense. Instead, use a filter to get the row you want:
SELECT *
FROM contenstants c JOIN
results r
ON r.result_contestant = c.contestant_id
WHERE r.result_discipline = 1 AND c.contestant_category = 1 AND
r.value = (select min(r2.value)
from results r2
where r2.result_contestant = r.result_contestant and
r2.result_discipline = r.result_discipline
)
ORDER BY value DESC;
Note: I'm not sure if you want min() or max() in the subquery.

MySQL - 3 tables, is this complex join even possible?

I have three tables: users, groups and relation.
Table users with fields: usrID, usrName, usrPass, usrPts
Table groups with fields: grpID, grpName, grpMinPts
Table relation with fields: uID, gID
User can be placed in group in two ways:
if collect group minimal number of points (users.usrPts > group.grpMinPts ORDER BY group.grpMinPts DSC LIMIT 1)
if his relation to the group is manually added in relation tables (user ID provided as uID, as well as group ID provided as gID in table named relation)
Can I create one single query, to determine for every user (or one specific), which group he belongs, but, manual relation (using relation table) should have higher priority than usrPts compared to grpMinPts? Also, I do not want to have one user shown twice (to show his real group by points, but related group also)...
Thanks in advance! :) I tried:
SELECT * FROM users LEFT JOIN (relation LEFT JOIN groups ON (relation.gID = groups.grpID) ON users.usrID = relation.uID
Using this I managed to extract specified relations (from relation table), but, I have no idea how to include user points, respecting above mentioned priority (specified first). I know how to do this in a few separated queries in php, that is simple, but I am curious, can it be done using one single query?
EDIT TO ADD:
Thanks to really educational technique using coalesce #GordonLinoff provided, I managed to make this query to work as I expected. So, here it goes:
SELECT o.usrID, o.usrName, o.usrPass, o.usrPts, t.grpID, t.grpName
FROM (
SELECT u.*, COALESCE(relationgroupid,groupid) AS thegroupid
FROM (
SELECT u.*, (
SELECT grpID
FROM groups g
WHERE u.usrPts > g.grpMinPts
ORDER BY g.grpMinPts DESC
LIMIT 1
) AS groupid, (
SELECT grpUID
FROM relation r
WHERE r.userUID = u.usrID
) AS relationgroupid
FROM users u
)u
)o
JOIN groups t ON t.grpID = o.thegroupid
Also, if you are wondering, like I did, is this approach faster or slower than doing three queries and processing in php, the answer is that this is slightly faster way. Average time of this query execution and showing results on a webpage is 14 ms. Three simple queries, processing in php and showing results on a webpage took 21 ms. Average is based on 10 cases, average execution time was, really, a constant time.
Here is an approach that uses correlated subqueries to get each of the values. It then chooses the appropriate one using the precedence rule that if the relations exist use that one, otherwise use the one from the groups table:
select u.*,
coalesce(relationgroupid, groupid) as thegroupid
from (select u.*,
(select grpid from groups g where u.usrPts > g.grpMinPts order by g.grpMinPts desc limit 1
) as groupid,
(select gid from relations r where r.userId = u.userId
) as relationgroupid
from users u
) u
Try something like this
select user.name, group.name
from group
join relation on relation.gid = group.gid
join user on user.uid = relation.uid
union
select user.name, g1.name
from group g1
join group g2 on g2.minpts > g1.minpts
join user on user.pts between g1.minpts and g2.minpts

MySQL Left Joins

EDIT: OK, think I need to be clearer - I'd like the result to show all the 'names' that appear in the table acme, against the counts (if any) from the results table. Hope that makes sense?
Having a huge issue and my brain isn't working as it should.
All I want to do is, in a single statement via a join, count the number of rows for a common field.
SELECT name, COUNT(name) as Count FROM acme
SELECT name, COUNT(name) as Total FROM results
I'm sure it should be something like this...
SELECT acme.name, COUNT(acme.name) As Count,
COUNT(results.name) as Total
FROM acme
LEFT JOIN results ON acme.name = results.name
GROUP BY name
ORDERY BY name
But it doesn't bring back the correct counts.
Thoughts, where am I going wrong...this, I know, will be very very obvious.
H.
From your feedback, this will get what you want. You need to FIRST get unique names / counts from the "ACME" file first... THEN join that to the results table for count of records from that, otherwise, you would end up with a Cartesian result of counts. If ACME had Name "X" 5 times and Results had "X" 20 times, your total would be 100. The query below will actually result with a single row showing "X", 5, 20 which is what it appears you are looking for.. (for however many names exist in ACME).
I've changed to a LEFT join in case there are names in the ACME table that DO NOT exist in the RESULTS table, it won't drop them from your final answer
select
JustACME.Name,
JustACME.NameCount,
COALESCE( COUNT( * ), 0 ) as CountFromResultsTable
from
( select a.Name
count(*) as NameCount
from
acme a
group by
a.Name ) JustACME
LEFT JOIN results r
on JustACME.Name = r.Name
group by
JustACME.Name
It looks like it's because of the join, it's screwing with your counts. Try running the join with SELECT * FROM... and look at the resulting table. The problem should be obvious from there. =D
Yes, your join (inner or outer, doesn't matter) is messing with your results.
In fact, it is likely returning the product of rows with the same name, rather than the sum.
What you want to do is sum the rows from the first table, sum the rows from the second table, and join that.
Like this:
Select name, a.count as Count, r.count as Total
From (select name, count(*) from acme group by name) a
Left join (select name, count(*) from results group by name) r using (name)
I do not see why you forbid using two statements this just complicates everything.
The only reason I see for this is to get the two results into one answer.
I do not know if the latter would work but I would try this:
SET #acount = (SELECT count(DISTINCT name) FROM acme);
SELECT count(DISTINCT name) as Total, #acount as Count FROM results
I would post this as one query and (hopefully) get back the correct results. Let me note, that it is not clear from you question if you want to know how often every name doubles or if you want to count unique names.

mysql intersection, comparison, opposite of UNION?

I'm trying to compare two set of resutls aving hard time to undesrtand how subqueries work and if they are efficient. I'm not gonna explain all my tables, but just think i have apair of arrays...i might do it in php but i wonder if i can do it in mysql right away...
this is my query to check how many items user 1 has in lists he owns
SELECT DISTINCT *
FROM list_tb
INNER JOIN item_to_list_tb
ON list_tb.list_id = item_to_list_tb.list_id
WHERE list_tb.user_id = 1
ORDER BY item_to_list_tb.item_id DESC
this is my query to check how many items user 2 has in lists he owns
SELECT DISTINCT *
FROM list_tb
INNER JOIN item_to_list_tb
ON list_tb.list_id = item_to_list_tb.list_id
WHERE list_tb.user_id = 1
ORDER BY item_to_list_tb.item_id DESC
now the problem is that i would intersect those results to check how many item_id they have in common...
thanks!!!
Unfortunately, MySQL does not support the Intersect predicate. However, one way to accomplish that goal would be to exclude List_Tb.UserId from your Select and Group By and then count by distinct User_Id:
Select ... -- everything except List_Tb.UserId
From List_Tb
Inner Join Item_To_List_Tb
On List_Tb.List_Id = Item_To_List_Tb.List_Id
Where List_Tb.User_Id In(1,2)
Group By ... -- everything except List_Tb.UserId
Having Count( Distinct List_Tb.User_Id ) = 2
Order By item_to_list_tb.item_id Desc
Obviously you would replace the ellipses with the actual columns you want to return and on which you wish to group.