Hopefully I can explain this well. I'm trying to pull in my database the max points, assists, and rebounds from one column and then based on that number, grab the game number (gid) where each of those numbers came from. I started it with something simple, thinking I could get it, but the gid it grabs is just the first id for that particular season, not for the right game in any case.
SELECT gid, sid, max(points), max(assists), max(rebounds)
FROM game_stats_lakers
WHERE playerid = 2
GROUP By gid
I want to get the gid (which is the game id) for the max points, assists, and rebounds which most likely will be different for each. I can't seem to figure out how to pull any gid correctly.
Thanks for any help!
Hmmm . . . Is this what you want?
(select gsl.*
from game_stats_lakers gsl
where playerid = 2
order by gsl.points desc
limit 1
) union all
(select gsl.*
from game_stats_lakers gsl
where playerid = 2
order by gsl.assists desc
limit 1
) union all
(select gsl.*
from game_stats_lakers gsl
where playerid = 2
order by gsl.rebounds desc
limit 1
) ;
Related
Please have a look at this fiddle.
https://www.db-fiddle.com/f/71CxYHKkzwmXJnovzpFheV/7
I'm trying to accomplish 2 things:
How do I get the length and date of the LATEST STREAK OF CORRECT GUESSES (meaning Result = Guess) without any skipped dates? In this case, it would be 4, starting from 2021-01-05 to 2021-01-08. (Although 2021-01-03 is correct, because there was no guess on 2021-01-04, it should not be included).
How do I get the length and date of the LONGEST STREAK OF CORRECT GUESSES OF ALL TIME? Again meaning Result = Guess, but can be anywhere in the table. Let's say it's 10 from 3 months ago.
To further complicate things, guesses can be made by multiple users AND there will be multiple results (for different game categories for example) on the same day. So the table above is for one user and one game category. I think I can handle this if I can get some guidance on the goals up above.
This is beyond my understanding. Any and all help would be appreciated.
EDIT: I've changed the table to show that the date is not always sequential. Also, I was informed that I should be using MySQL 8.0 for this task as using variables is not good practice for this problem.
Edit: Using the window functions, starting to get somewhere:
Please check the fiddle. It's pretty close to what I'm trying to get to, but the '4' in the total column should be a 1. In other words, the "sum" should restart. Not sure how to achieve this, because it's clear that the window function will group based on the conditions, breaking the order and thus the streak.
Updated: I've updated the fiddle per #The Impaler's request. The table here is more representative to what I'm actually working with (still not exact, but much closer).
Since this new fiddle is more representative, I'll also explain my final goal. I'd also like to get the streak for each game_type. The way I've been comparing game_type result on a given day to "community" (basically all the users) guess is by summing all the 0's and 1's for each game_type on that date from all the users and then using whichever greater as the 'guess'. This way, I can get how the "community" is doing as a whole. This works for individual dates, but to do a streak, I'm not sure.
Update 2
So this is as far as I've got:
https://www.db-fiddle.com/f/71CxYHKkzwmXJnovzpFheV/11
I tried to do a nested window function but that's not allowed. I have the proper groupings and column for when guess = result. Now I need help figuring out the streak within the groups.
This is a typical "Gaps & Islands" problem. Once you assemble the islands the query becomes easy.
For example, for a single user, as stated in the fiddle you can get the LONGEST STREAK by doing:
with
i as (
select
min(dayt) as starting_day,
max(dayt) as ending_day,
count(*) as streak_length
from (
select *, sum(beach) over(order by dayt) as island
from (
select *,
guess = result as inland,
case when (guess = result) <> (
lag(guess) over(order by dayt) = lag(result) over(order by dayt))
then 1 else 0 end as beach
from mytable
) x
where inland = 1
) y
group by island
)
select *
from i
order by streak_length desc
limit 1;
Result:
starting_day ending_day streak_length
------------- ----------- -------------
2021-01-06 2021-01-08 3
To get the LATEST STREAK you just need to change the ORDER BY clause at the end as shown below:
with
i as (
select
min(dayt) as starting_day,
max(dayt) as ending_day,
count(*) as streak_length
from (
select *, sum(beach) over(order by dayt) as island
from (
select *,
guess = result as inland,
case when (guess = result) <> (
lag(guess) over(order by dayt) = lag(result) over(order by dayt))
then 1 else 0 end as beach
from mytable
) x
where inland = 1
) y
group by island
)
select *
from i
order by ending_day desc
limit 1;
Result (same result as before):
starting_day ending_day streak_length
------------- ----------- -------------
2021-01-06 2021-01-08 3
See running example at DB Fiddle.
Note: You can remove the LIMIT clause at the end to see all the islands, not just the selected one.
For multi-users it's just a matter of modifying the windows (adding partitioning) and the rest of the query remains the same. If you provide a fiddle for multi-users I can add the solution as well.
So, it took a while, but thanks to #The Impaler providing me the basis and the link below, I was able to solve the problem.
https://www.red-gate.com/simple-talk/sql/t-sql-programming/efficient-solutions-to-gaps-and-islands-challenges/
Here is the full solution:
with GAME_LOG as (
select
*,
guess = result as correct,
lag(case when (guess = result) then 1 else 0 end) over(partition by user_id, game_type) as previous_game_result,
lead(case when (guess = result) then 1 else 0 end) over(partition by user_id, game_type) as next_game_result,
row_number() over(partition by user_id, game_type order by dayt DESC) as ilocation
from mytable
),
CTE_ISLAND_START as (
select
*,
row_number() over(partition by user_id, game_type order by dayt DESC) as inumber,
dayt as island_start_time,
ilocation as island_start_location
from GAME_LOG
where correct = 1 AND
(previous_game_result <> 1 OR previous_game_result is null)
),
CTE_ISLAND_END as (
select
*,
row_number() over(partition by user_id, game_type order by dayt DESC) as inumber,
dayt as island_end_time,
ilocation as island_end_location
from GAME_LOG
where correct = 1 AND
(next_game_result <> 1 OR next_game_result is null)
)
select
CTE_ISLAND_START.user_id,
CTE_ISLAND_START.game_type,
CTE_ISLAND_START.island_start_time as streak_end,
CTE_ISLAND_END.island_end_time as streak_start,
cast(CTE_ISLAND_END.island_end_location as signed) -
cast(CTE_ISLAND_START.island_start_location as signed) + 1 as streak
from CTE_ISLAND_START
inner join CTE_ISLAND_END
on CTE_ISLAND_START.inumber = CTE_ISLAND_END.inumber AND
CTE_ISLAND_START.user_id = CTE_ISLAND_END.user_id AND
CTE_ISLAND_START.game_type = CTE_ISLAND_END.game_type
This will give all the streaks for each user_id, each game_type, as well as the start and end dates of the streak.
You can simply add a WHERE clause to filter by game_type and user_id.
Here's the fiddle with slightly updated dataset.
Fiddle
I have a DB Table user_points which contains user's points and I am trying to calculate ranking based on points. It is working fine for all users except users having 1 point.
If user have 1 point it is showing it's rank as 0 but it should display it's rank as last or in last numbers like: 12083 etc.
Higher points are, ranking should be higher as well. For example:
1000 points = rank 1
1 point = rank 1223
Following is the query.
SELECT id, mobileNo, points,
FIND_IN_SET( points, (SELECT GROUP_CONCAT( points ORDER BY points DESC )
FROM users_points )) AS rank
FROM users_points
WHERE mobileNo = '03214701777'
What should I change to fix it?
SELECT a.id, a.mobileNo, a.points,
IFNULL((SELECT COUNT(*) AS rank
FROM users_points b
WHERE b.points<a.points), 0)+1 as rank
FROM user_points a
WHERE a.mobileNo = '03214701777'
Seems to be what you are looking for. While it is still very innefficient it is better than your approach using FIND_IN_SET(). If you really want to use FIND_IN_SET() then you need to pad the scores to a consistent width and divide by the width+1 to get the rank.
I'm trying to make a query and I already searched for an answer on stackof but didn't find one matching my needs.
I have a table named player in which there are two columns, "nickname" and "score".
I use this query to get the top 5 players:
SELECT nickname, score
FROM player
ORDER BY score DESC LIMIT 5;
and I got this as the answer:
nickname - score:
zod - 30
ciao - 20
jiji - 20
mayina - 20
jon - 0.
Now, I'd like to have the rank of a single player, let's say "jiji" and get 3 as a result, because it's the third result in the list.
I tried many queries like
SELECT COUNT(*) AS rank
FROM player
WHERE score >= (SELECT score FROM player WHERE nickname = 'jiji')
but they always return 4 for "jiji" or "ciao", which is the rank of the last player who gets 20 as score in that table.
How can I get to have 3 for "jiji", instead? Thank you very much.
Try this:
SET #rank=0;
SELECT *
FROM (SELECT #rank:=#rank+1, nickname, score
FROM player
ORDER BY score
DESC) AS t
WHERE t.nickname = 'jiji';
Correct comment about this not being stable in case of score ties. To make it stable, we can change the sorting to be based on score and then nickname:
SELECT *
FROM (SELECT #rank:=#rank+1, nickname, score
FROM player
ORDER BY score, nickname
DESC) AS t
WHERE t.nickname = 'jiji';
Using commonly used definitions, the rank for jiji would be:
SELECT count(*) + 1 AS rank
FROM player
WHERE score > (SELECT score FROM player WHERE nickname = 'jiji');
This returns "2", because there are ties when score = 30.
If you want the rank to be stable and different for each row, you need an additional key. An obvious key (in this case) is nickname:
SELECT count(*) AS rank
FROM player p CROSS JOIN
(SELECT score FROM player WHERE nickname = 'jiji') s
WHERE p.score > s.score or
(p.score = s.score and p.nickname <= 'jiji');
Let's say I have a table like this:
Player Score
A 5
B 4
A 3
B 2
A 1
B 1
A 2
B 3
A 4
B 5
I need an SQL query that will return the three highest scores per player in descending order "grouped" by player i.e.
Player Score
A 5
A 4
A 3
B 5
B 4
B 3
Very grateful for any pointers.
This is old-fashioned (read: basic sql) way of producing top-n per group. You might join the table to itself on group condition (here it is player) and pick records with higher score on right side; if there are three or less such records, the row is one of top n rows per group.
select player.player, player.score
from Player
left join Player p2
on p2.player = player.player
and p2.score > player.score
group by player.player, player.score
having count(distinct p2.score) < 3
order by 1, 2 desc
Alternative version you might check, using not exists:
select player, score
from player
where not exists
(
select p2.player
from Player p2
where p2.player = player.player
and p2.score > player.score
group by p2.player
having count(distinct p2.score) > 3
)
order by 1, 2 desc
This two versions differ in presentation of ties - while first one returns one row (by nature of group by) and needs to be joined back to original table to show all records, second one works directly from original table showing all data and ties at once.
You can find Demo at Sql Fiddle.
in SQL server:
select p.player, p.score
from PS p
where p.score in (select top 3 score from PS
where player = p.player order by score desc)
order by p.player asc, p.score desc
in MySql:
select p.player, p.score
from PS p
where p.score in (select score from PS
where player = p.player order by score desc limit 3)
order by p.player asc, p.score desc
I think what you are looking for can be found here:
http://www.sql-ex.ru/help/select16.php
Basically, the best solution uses the RANK function. Here is the example code from the site:
SELECT maker, model, type FROM
(
SELECT maker, model, type, RANK() OVER(PARTITION BY type ORDER BY model) num
FROM Product
) X
WHERE num <= 3
You would just need to modify the Partition By section to order by your score in descending order.
EDIT
Based upon the information that you will be using MySQL, you will need to make some modifications to the above query (which works with Microsoft SQL). You need to replace the RANK function with your own RANK implementation. It isn't that hard. Complete instructions can be found here:
http://thinkdiff.net/mysql/how-to-get-rank-using-mysql-query/
That will show you how to implement a counter that can give you a rank number.
Depending on what DBMS you use, you may be able to use row_number in some form
In SQL Server 2008 you can use
create table #player
( Player char, Score int )
insert into #player (Player, Score) Values
('A',5),('B',4),('A',3),('B',2),('A',1),('B',1),('A',2),('B',3),('A',4),('B',5)
select * from #player
select Player, Score from
(
select *, ROW_NUMBER() over(partition by Player order by Score desc) as rowNo
from #player
) as tmp
where tmp.rowNo <= 3
drop table #player
In my table i have team and points column and I want to get top 5 teams .Teams with same points should be grouped and consider as one of the ranks so if 5 teams are having same points then all should come as one of the rank and next suceeding records according to team points
TRY
SELECT DISTINCT(point), team
FROM tableTeam
ORDER BY points DESC LIMIT 5
SELECT team,
points,
(SELECT COUNT(*)
FROM teams t2
WHERE t2.points > t1.points) + 1 rank
FROM teams t1
ORDER BY points DESC
LIMIT 5
There's no window functions in MySQL, so you'll want to extract the rank in your scripts.
Also, if I'm making sense of your ranking criteria, you're actually interested in getting the top 5 teams plus any additional teams that might have the same number of points as that in the 5th rank.
If so, your limit should be applied to a subquery on the point criteria:
select name, points
from teams
where points >= (
select points
from teams
order by points desc
limit 1 offset 4
)
order by points desc, name
If not, a simple order by/limit will do:
select name, points
from teams
order by points desc, name
limit 5