How to give rank in query according to points - mysql

The below query is working absolutely fine, as I need. All the user get unique RANKS (User of same points should not get same rank)
SELECT
id,
first_name,
email,
(SELECT
rank
FROM ( SELECT
#rownum:=#rownum+1 rank,
u.id AS user_id,
points
FROM
user_master u, (SELECT #rownum:=0) r
ORDER BY
points
DESC) AS tmp
WHERE
user_id = um.id) AS Rank,
registered_date AS registered,
um.points as Points
FROM
user_master um
ORDER BY
um.id ASC
Now I want to make view for this query, it gives me error message
View's SELECT contains a subquery in the FROM clause
I've also tried first to make a view of user ranks to merge 2 different views. The below query gives perfect rankings of user but when I try to make view of this:
SELECT
#rownum:=#rownum+1 rank,
id AS user_id,
points
FROM
user_master u, (SELECT #rownum:=0) r
ORDER BY
points
DESC
..it gives me error message:
View's SELECT contains a variable or parameter
Is there any other way to apply rank in this query (Rank must be unique even if points are same).

Give this a go:
create view test_view as SELECT t.id,t.first_name,t.email,
(select sum(case when t1.points > t.points then 1
when t1.points = t.points and t1.id < t.id then 1
else 0 end) from user_master t1)+1 as rank, t.registered_date AS registered,
t.points as Points
from user_master t
order by points desc;

Related

Invalid use of group function for mysql

I am getting this error but I don't understand why my query doesn't work. Can someone please give me a hand?
The question is this:
SELECT activity
FROM Friends
GROUP BY activity
HAVING COUNT(activity) > MIN(COUNT(activity))
AND COUNT(activity) < MAX(COUNT(activity))
My idea is that as long as the count of the activity is larger than the activity that has the minimum count and less than the activity that has the maximum count, it should be returned. But I am having "Invalid use of group function" error which I don't understand. One possible thing that I could think of is that the parts that I am comparing with the COUNT(activity) have to be a number that is selected from the table instead of a part that has "MIN" or "MAX". But I don't understand why as they both look like the same number to me.
If your version of MySql is less than 8.0, the following will work:
select activity, count(*) as cnt from Friends group by activity
having cnt not in (
select max(cnt) as cnt from (
select activity, count(*) as cnt from Friends group by activity
) sq1
union
select min(cnt) as cnt from (
select activity, count(*) as cnt from Friends group by activity
) sq2
);
activity
cnt
Singing
2
View on DB Fiddle
Since the above SQL references the same select 3 times, namely select activity, count(*) as cnt from Friends group by activity, you might consider creating a quasi-temporary table (but there is the overhead in creating such a table to consider -- for the number of rows you actually presented, this would run more slowly):
create table t as select activity, count(*) as cnt from Friends group by activity;
select activity from t where cnt not in (
select max(cnt) as cnt from t
union
select min(cnt) as cnt from t
);
drop table t;
activity
Singing
View on DB Fiddle
If you just want data from friends table then you can use the analytical function (My sql 8.0 or higher) as follows:
select activity from
(SELECT activity, dense_rank() over (order by count(*)) as rn_asc,
dense_rank() over (order by count(*) desc) as rn_desc
FROM Friends
GROUP BY activity)
where rn_asc <> 1 and rn_dsc <> 1

Getting position of a user in a mySql database

I'm trying to get the position of a specific user in a leaderboard for basketballScore. There is only one table in the database.
Here is the image of the database in mySQL
I imagine it would be something like this:
SELECT userID, SUM(points) as '`basketballScore`'
FROM user
ORDER BY SUM(points) DESC
You need a group by clause:
select userID,
SUM(score) as basketballScore
from user
group by userID
order by basketballScore desc
Also note that I reused the alias basketballScore in the order by clause.
If you need to create ranks, you can use user variables:
set #rank := 0;
select #rank := #rank + 1 as rank,
userID,
SUM(score) as basketballScore
from user
group by userID
order by basketballScore desc
You can get the rank of a specific user by doing:
select 1 + count(*) as rank
from user u
where u.basketballScore > (select u2.basketballScore from user u2 where u2.userId = #userId);

How do I make a sql sentence to do order by before distinct?

For example, I have this order table, and it has columns: order_id, user_id, create_time, city_id.
Now I want to get the entry of an user's most recent order so basically what I want to do is:
select distinct(order.user_id), city_id
from order
where city_id != 0
order by create_time desc
But as far as I know distinct will run before order by, which means there's already only one user_id left for each user before it reaches order by, so what do I do to make order by run first?
Have a sub-query that returns each user's most recent create_time. JOIN with that result.
select o1.user_id, o1.city_id
from order o1
join (select user_id, max(create_time) as newest_create_time
from order
where city_id != 0
group by user_id) o2
on o1.user_id = o2.user_id and o1.create_time = o2.newest_create_time
where o1.city_id != 0

Select most occurring value in MySQL

I'm looking for a way to select the most occurring value, e.g. the person who posted most for each thread;
SELECT MOST_OCCURRING(user_id) FROM thread_posts GROUP BY thread_id
Is there a good way to do this?
If you want a count on a per thread basis, I think you can use a nested query; grouping by thread first and then by user:
SELECT thread_id AS tid,
(SELECT user_id FROM thread_posts
WHERE thread_id = tid
GROUP BY user_id
ORDER BY COUNT(*) DESC
LIMIT 0,1) AS topUser
FROM thread_posts
GROUP BY thread_id
This will tabulate the occurrences of user_id per thread
SELECT thread_id, user_id, COUNT(*) as postings
FROM thread_posts
GROUP BY thread_id, user_id
But you only wish to select the top user for each thread
SELECT thread_id, user_id, postings
FROM (
SELECT thread_id, user_id, COUNT(*) as postings
FROM thread_posts
GROUP BY thread_id, user_id
)
HAVING postings = max(postings)
which is equivalent to
SELECT thread_id, user_id, COUNT(*) as postings
FROM thread_posts
GROUP BY thread_id, user_id
HAVING postings = max(postings)
The HAVING keyword is usually used with an aggregation operation to cherry-pick the aggregated output lines that satisfy the conditions in the HAVING clause.
The HAVING clause is different from the the WHERE clause, wherein the HAVING clause filters resultant output of a query. Whereas, the WHERE clause filters on the input data of a query.
Since theHAVING clause filters the resultant output of a query, it must appear after the ORDER BY and GROUP BY clauses.
There's numerous examples if you check the questions under the "greatest n per group" tag. But in this case, you don't define how you want to handle ties - what if two or more users have the same count value?
SELECT DISTINCT
tp.thread_id,
tp.user_id
FROM THREAD_POSTS tp
JOIN (SELECT t.thread_id,
t.user_id,
COUNT(t.user_id) AS occurrence,
CASE
WHEN #thread != t.thread_id THEN #rownum := 1
ELSE #rownum := #rownum + 1
END AS rank,
#thread := t.thread_id
FROM THREAD_POSTS t
JOIN (SELECT #rownum := 0, #thread := -1) r
GROUP BY t.thread_id, t.user_id
ORDER BY t.thread_id, occurrence DESC) x ON x.thread_id = tp.thread_id
AND x.user_id = tp.user_id
AND x.rank = 1

Automate MySQL fields (like Excel)

Say I have a table like ID, NAME, SCORE. Now normally, to get the rankings of the teams, I'd select all and order by. Sometimes though, I don't want to know all the rankings, just the ranking of one team. If I added a column RANK, is there any way for MySQL to automatically fill in those values for me based off of SCORE? (I believe MS Excel has this capability)
and if so, how does it handle ties?
thanks
You can calculate the rankings when you make your query:
SELECT * FROM (
SELECT teams.*, #rownum := #rownum + 1 AS rank
FROM teams, (SELECT #rownum := 0) T1
ORDER BY score DESC) T2
WHERE id = 1
It works by initializing a variable called rownum to 0 and then iterating over the rows in order of decreasing score. For each team the rownum is increased and the team is assigned a rank based on the current value of rownum. The outer select applies a where clause so that only one row is returned.
Here is an improved version that assigns the same rank to teams that have tied scores:
SELECT id, name, teams.score, rank FROM (
SELECT score, #rownum := #rownum + 1 AS rank
FROM (SELECT DISTINCT(score) FROM teams) T1, (SELECT #rownum := 0) T2
ORDER BY score DESC) T3
JOIN teams ON T3.score = teams.score
If this isn't fast enough for you, then use a trigger instead.
looks like I'm looking for a MySQL trigger
Get All Teams:
SELECT
s1.name,
s1.score,
COUNT(s2.name) + 1 AS rank
FROM scores s1
LEFT OUTER JOIN scores AS s2 ON (s1.score < s2.score)
GROUP BY s1.name
ORDER BY COUNT(s2.name)
Get One Team ('The Canucks'):
SELECT
s1.name,
s1.score,
COUNT(s2.name) + 1 AS rank
FROM scores s1
LEFT OUTER JOIN scores AS s2 ON (s1.score < s2.score)
GROUP BY s1.name
HAVING s1.name = 'The Canucks'
ORDER BY COUNT(s2.name)
The method shown in the above examples get the ranking dynamically (not filling a regular or temp table).
Note: both of these assume a given team only exists once in scores table for the rank value to be correct.