MySQL Advanced Ranks Query - mysql

I have a table that looks like this:
map uid time name
'first' 1 5.0 'Jon'
'first' 3 4.9 'Robin'
'second' 1 2.0 'Jon'
'first' 2 5.3 'Max'
'second' 3 2.1 'Robin'
I am currently selecting the values using this:
SELECT records.* FROM `records` WHERE `uid` = '3' ORDER BY `records`.`time` ASC
Now obviously, I have multiple uids for different maps. How would I find the rank of every user out of total ranks? I know I can find total ranks of the map by using COUNT(DISTINCT map). However, I am having issues selecting a specific user and their rank in the map. Any help would be appreciated!
EDIT:
Desired output when selecting uid 3 is as follows:
map uid time name position totalposition (totalposition would be COUNT(DISTINCT map))
'first' 3 4.9 'Robin' 2 3
'second' 3 2.1 'Robin' 2 2

Use the following query : -
mysql> set #pos = 0; select records.*, #pos:=#pos+1 as position from records order by time desc;
Output :
+--------+------+------+-------+--------------+
| map | uid | time | name | position |
+--------+------+------+-------+--------------+
| first | 2 | 5.30 | Max | 1 |
| first | 1 | 5.00 | jon | 2 |
| first | 3 | 4.90 | Robin | 3 |
| second | 3 | 2.10 | Robin | 4 |
| second | 1 | 2.00 | Jon | 5 |
+--------+------+------+-------+--------------+
And now, to recieve position of a particular :
mysql> set #pos = 0; select * from (select records.*, #pos:=#pos+1 as position
mysql> from records order by time desc) as t where uid = 3;
Output :
+--------+------+------+-------+----------+
| map | uid | time | name | position |
+--------+------+------+-------+----------+
| first | 3 | 4.90 | Robin | 3 |
| second | 3 | 2.10 | Robin | 4 |
+--------+------+------+-------+----------+

Related

Group by Id with greatest value

I have a simple table where I'm calculating a "level value" dynamically via SELECT with repeated Ids in this table, what I'm trying to do is to Group the rows Ids by the greatest level value.
Fiddle: https://www.db-fiddle.com/f/2Pyfi2PMV8eaQbDt2uWQjc/2
I already tried using CASE in Group and Order but does not work well.
I already tried using MAX() but I get the score of the second value instead of the fourth. What I'm trying to get is a result like:
| id | score | level |
| -- | ------- | ----- |
| 1 | [] | 0 |
| 16 | [1,2,4] | 3 |
| 17 | [1] | 1 |
Is there any way to group the Ids but in case there's a repeated Id get the one with the greatest level?
Thanks.
You can use group by like this :
select test.id , min(test.score) as score,
(CASE
WHEN JSON_LENGTH(min(test.score)) = 3 THEN 3
WHEN JSON_LENGTH(min(test.score)) = 2 THEN 2
WHEN JSON_LENGTH(min(test.score)) = 1 THEN 1
ELSE 0
END) as level
from test group by test.id
and the result is :
|id | score | level |
|---|---------|-------|
|1 | [] | 0 |
|16 | [1,2,4] | 3 |
|17 | [1] | 1 |

Ordering values by custom criteria

I have two tables A and B table:
Table - A - represents basic information of persons
emp_id | email | name
----------------------------------------
1 | abc#gmail.com | john
2 | dbc#gmail.com | john1
3 | cbc#gmail.com | john2
4 | xbc#gmail.com | john3
5 | xac#gmail.com | john4
Table - B represents the locations handled by persons
john is handling Region and Zone
john1 is handling Area and Territory and so on...
Sequence of locationType is as follows : Region->Zone->Area->Territory
Regions is having higher priority then comes zone and so on..
id | emp_id | locationType
--------------------
1 | 1 | Region
2 | 2 | Area
3 | 3 | Area
4 | 4 | Territory
5 | 1 | Zone
6 | 2 | Territory
7 | 5 | Zone
8 | 5 | Area
I want to fetch those persons who are handling the higher locationType.
Suppose john is handling Region and zone so i want to display Region as Region is of higher priority and similarly john1 is handling Territory and Area so i want to display only Area as because Area is of higher priority
My Desired Output:
id | emp_id | name | locationType
----------------------------------------
1 | 1 | john | Region
5 | 5 | john4 | Zone
3 | 3 | john1 | Area
4 | 4 | john2 | Area
4 | 4 | john3 | Territory
What I am getting
id | emp_id | name | locationType
----------------------------------------
1 | 1 | john | Region
1 | 1 | john | Zone
5 | 5 | john4 | Zone
5 | 5 | john4 | Area
2 | 2 | john1 | Area
3 | 3 | john2 | Area
4 | 4 | john3 | Territory
4 | 4 | john3 | Territory
You can use field() to turn the locations into numbers. What you want is the minimum location based on this ordering.
You can obtain this information per employee using a correlated subquery:
select b.*
from b
where field(b.locationType, 'Region', 'Zone', 'Area', 'Territory') =
(select min(field(b2.locationType, 'Region', 'Zone', 'Area', 'Territory'))
from b b2
where b2.emp_id = b.emp_id
);
Adding the extra columns from a is just a matter of joining in the table.
use case when in order clause
order by (case locationType when 'Region' then 1
when 'Zone' then 2
when 'Area' then 3
when 'Territory' then 4
else 5 end )
To Resolve the Issue Pemanently just Follow the following steps. It will help you in data normalization also.
1 Create A new table that has LocaitonType name and id and Insert your location type as the order you want.
CREATE TABLE [dbo].[Table_C](
[LocationType_Id] [int] IDENTITY(1,1) NOT NULL,
[name] [nvarchar](50) NULL )
Insert Into [Table_C] (name) values('Region')
Insert Into [Table_C] (name) values('Zone')
Insert Into [Table_C] (name) values('Area')
Insert Into [Table_C] (name) values('Territory')
2.Alter your table b LocationType column Data type to int.
Alter Table Table_B
Alter column locationType int not null
Now Insert id from Table_c to Table_B Locationtype Column.
and use the below query to get the desired output.
Select Table_B.id,Table_A.emp_id,Table_A.name,Table_C.name as locationType from Table_B inner join Table_A on Table_A.emp_id=Table_B.emp_id
inner join Table_C on Table_C.LocationType_Id=Table_B.locationType
order by Table_C.LocationType_Id

SQL order by match to specific row

I have a example table below. I am trying to create a SQL query that gets all user_ids besides user_id of the current user and then orders by number of matches to the row with the current user_id
For example, if the user has a user_id of '1', I want to get all of the user_ids corresponding with the rows of id 2-8, and then order the user_ids from most matches to the row of the current user to least matches with the row of the current user
Let's say var current_user = 1
Something like this:
SELECT user_id
FROM assets
WHERE user_id <> `current_user` and
ORDER BY most matches to `current_user`"
The output should get 7,8,3,9,2
I would appreciate anyone's input on how I can effectively achieve this.
Table assets
+----------+---------+-------+--------+-------+
| id | user_id | cars | houses | boats |
+----------+---------+-------+--------+-------+
| 1 | 1 | 3 | 2 | 3 |
| 2 | 8 | 3 | 2 | 5 |
| 3 | 3 | 3 | 2 | 2 |
| 4 | 2 | 5 | 1 | 5 |
| 5 | 9 | 5 | 7 | 3 |
| 8 | 7 | 3 | 2 | 3 |
+----------+---------+-------+--------+-------+
I think you can just do this:
select a.*
from assets a cross join
assets a1
where a1.user_id = 1 and a.user_id <> a1.user_id
order by ( (a.cars = a1.cars) + (a.houses = a1.houses) + (a.boats = a1.boats) ) desc;
In MySQL, a boolean expression is treated as an integer in a numeric context, with 1 for true and 0 for false.
If you want to be fancier, you could order by the total difference:
order by ( abs(a.cars - a1.cars) + abs(a.houses - a1.houses) + abs(a.boats - a1.boats) );
This is called Manhattan distance, and you would be implementing a version of a nearest neighbor model.

mysql Quiz leaderboard filter by points, time taken

I have a quiz report table which shows a report for every quiz a user takes. I need to create a leaderboard from this, which shows the top users best score, filtering by points and then time taken.
here is a link to a sql fiddle http://sqlfiddle.com/#!2/65fbf0/1
I am really struggling as i need to filter the results by two columns for one user, my ideal result would be
Results for Quiz id 1
---------------------------------------------------------------
| user_id | points | time_spend | start_dt | quiz_id |
| 1 | 3 | 0.5 | May,15 2015| 1 |
| 2 | 3 | 0.8 | May,15 2015| 1 |
| 3 | 2 | 0.5 | May,15 2015| 1 |
Then a separate query for all quiz's showing the results from the last week
Results from all Quizzs
---------------------------------------------------------------
| user_id | points | time_spend | start_dt | quiz_id |
| 1 | 3 | 0.5 | May,15 2015| 1 |
| 2 | 3 | 0.8 | May,13 2015| 3 |
| 3 | 2 | 0.5 | May,12 2015| 2 |
You can sort on multiple columns like this:
select *
from QuizReport
where quiz_id = 1
order by points desc, time_spend asc;
select *
from (
select *
from QuizReport
where start_dt >= subdate(curdate(), 7)
order by points desc, time_spend asc) a
group by user_id;
group_by user_id preserves the first row for every user_id. since the inner query sorts rows by score, the outer query will display best row for every user.

How do I self-join the result of an aggregate function in MySQL?

I have some data in the following format:
Table: MountHeights
ID | Height| Type | ItemCode | Context
--------------------------------------------
1 | 15 | max | BD1896-1W | exterior
2 | 12 | max | BD1896-1W | insect
3 | 18 | max | BD1896-1W | interior
4 | 13 | max | BD14120-1W | exterior
5 | 10 | max | BD14120-1W | insect
6 | 15 | max | BD14120-1W | interior
There are multiple rows for each of the item codes.
I'm trying to figure out a way to get max(Height) for each ItemCode where the Type='max', for some reason, I can't quite wrap my head around how I should reference the table to itself.
I'd like the result to be along the lines of:
Results:
max(Height)| ItemCode
---------------------------
18 | BD1896-1W
15 | BD14120-1W
How do I do that?
There is no need to self-reference the table here.
SELECT `ItemCode`, MAX( `height` )
FROM `MountHeights`
WHERE `Type`= 'max'
GROUP BY `ItemCode`
Example Fiddle
You can also do:
SELECT t1.ItemCode,max(t1.height) as height
from MountHeights as t1, test as t2
where t1.Type=t2.Type group by t1.Type