mysql grouped ranking with ties - mysql

so i have data in a table like this:
id total group_id
1897 738 1
2489 716 2
2325 715 3
1788 702 2
1707 699 3
2400 688 3
2668 682 2
1373 666 1
1494 666 1
1564 660 1
2699 659 1
1307 648 4
1720 645 4
2176 644 1
1454 644 4
2385 639 3
1001 634 2
2099 634 4
1006 632 1
2587 630 3
1955 624 3
1827 624 4
2505 623 4
2062 621 3
1003 618 1
2286 615 4
2722 609 4
how can i rank the ids per group based on the total and giving the same rank when there is a tie?
i have tried this solution below but it doesnt take care of the ties.
SELECT g1.admission_no
, g1.total
, g1.stream_id
, COUNT(*) AS rn
FROM ranktest AS g1
JOIN ranktest AS g2
ON (g2.total, g2.admission_no) >= (g1.total, g1.admission_no)
AND g1.stream_id = g2.stream_id
GROUP BY g1.admission_no
, g1.stream_id
, g1.total
ORDER BY g1.stream_id
, total ;
expected
id total group_id rank
1897 738 1 1
2489 716 2 1
2325 715 3 1
1788 702 2 2
1707 699 3 2
2400 688 3 3
2668 682 2 3
1373 666 1 2
1494 666 1 2
1564 660 1 3
2699 659 1 4
1307 648 4 1
1720 645 4 2
2176 644 1 4
1454 644 4 3
2385 639 3 4
1001 634 2 4
2099 634 4 4
1006 632 1 5
2587 630 3 5
1955 624 3 6
1827 624 4 5
2505 623 4 6
2062 621 3 6
1003 618 1 6
2286 615 4 7
2722 609 4 8

If original order is not very important you can start from:
http://sqlfiddle.com/#!9/a15a2/10
SELECT
ranktest.*,
IF(#rank IS NULL,#rank:=1, IF(#prev!=group_id,#rank:=1,#rank:=#rank+1) ) rank,
#prev:=group_id
FROM ranktest
ORDER BY group_id, total
but keep in mind that this is not very efficient query from performance perspective.

From the MySQL Manual Page entitled User-Defined Variables:
In the following statement, you
might think that MySQL will evaluate #a first and then do an
assignment second:
SELECT #a, #a:=#a+1, ...;
However, the order of evaluation for expressions involving user
variables is undefined.
This is why so few people write these answers correctly and safely. The object is not to hit the desired results once, but to do so again and again in real-world environments using best practices and guaranteed results.
If you don't read the Obligatory Doc, and implement it, your results are not guaranteed. There is no lazy way to do this and one shouldn't even bother :p
select id,total,group_id,rank
from
( select id,total,group_id,
#rn:=if( group_id!=#curr_grp, greatest(#grp_rank:=1,0),
if(total=#prev_grp_total,#grp_rank,greatest(#grp_rank:=#grp_rank+1,0) ) ) as rank,
#curr_grp:=group_id,
#prev_grp_total:=total
from trans01
cross join (select #grp_rank:=0,#curr_grp:=0,#prev_grp_total:=-1) xParams
order by group_id,total desc
)xDerived;
+------+-------+----------+------+
| id | total | group_id | rank |
+------+-------+----------+------+
| 1897 | 738 | 1 | 1 |
| 1373 | 666 | 1 | 2 |
| 1494 | 666 | 1 | 2 |
| 1564 | 660 | 1 | 3 |
| 2699 | 659 | 1 | 4 |
| 2176 | 644 | 1 | 5 |
| 1006 | 632 | 1 | 6 |
| 1003 | 618 | 1 | 7 |
| 2489 | 716 | 2 | 1 |
| 1788 | 702 | 2 | 2 |
| 2668 | 682 | 2 | 3 |
| 1001 | 634 | 2 | 4 |
| 2325 | 715 | 3 | 1 |
| 1707 | 699 | 3 | 2 |
| 2400 | 688 | 3 | 3 |
| 2385 | 639 | 3 | 4 |
| 2587 | 630 | 3 | 5 |
| 1955 | 624 | 3 | 6 |
| 2062 | 621 | 3 | 7 |
| 1307 | 648 | 4 | 1 |
| 1720 | 645 | 4 | 2 |
| 1454 | 644 | 4 | 3 |
| 2099 | 634 | 4 | 4 |
| 1827 | 624 | 4 | 5 |
| 2505 | 623 | 4 | 6 |
| 2286 | 615 | 4 | 7 |
| 2722 | 609 | 4 | 8 |
+------+-------+----------+------+

came up with this answer after googling a bit..not sure is its the best but it works for my case:
SELECT id, group_id, total,
#std:=CASE WHEN #grp <> group_id THEN concat(left(#grp:=group_id, 0), 1) ELSE if(#prev=total,#std,#std+1) END AS rn,#prev:=total
FROM
(SELECT #std:= -1) s,
(SELECT #grp:= -1,#prev:=null) c,
(SELECT *
FROM table
ORDER BY group_id, total desc
) s

Related

Numbering group results into unique identifiers

I am trying to create an query that results in unique identifiers (account number).
What I need to achieve is for each unique entry into Col1 to create a another row number / UI.
I have been attempting this with the below query
elect col1,col2 ,(DENSE_RANK() OVER( ORDER BY col1,col2)) as UI from [TABLE]
and this is what i have been getting:
col1 col2 UI
34 1 1
1973 448 2
355 3924 3
18709 8168 4
5201 9211 5
5762 9294 6
3864 10669 7
4914 12568 8
4914 12569 9
42465 921 10
but i need it to look like this:
col1 col2 UI
34 1 1
1973 448 2
355 3924 3
18709 8168 4
5201 9211 5
5762 9294 6
3864 10669 7
4914 12568 8
4914 12569 8
42465 921 9
You need to define a way to - more loosely - rank col2 e.g. using division
select
col1,col2
,DENSE_RANK() OVER( ORDER BY col1,col2/10) as newUI
from mytable
+----+-------+-------+-------+
| | col1 | col2 | newUI |
+----+-------+-------+-------+
| 1 | 34 | 1 | 1 |
| 2 | 355 | 3924 | 2 |
| 3 | 1973 | 448 | 3 |
| 4 | 3864 | 10669 | 4 |
| 5 | 4914 | 12568 | 5 |
| 6 | 4914 | 12569 | 5 |
| 7 | 5201 | 9211 | 6 |
| 8 | 5762 | 9294 | 7 |
| 9 | 18709 | 8168 | 8 |
| 10 | 42465 | 921 | 9 |
+----+-------+-------+-------+

I have three tables. I want to have one table. ReDesign my table

I got three tables:
call_center_telephone - TABLE NAME
id | sanal_gomdol | uilchilgee_lavlagaa | shine_serg_zahialga |.......| date
1 | 87 | 181 | 10 |.......| 2016-10-18
2 | 96 | 207 | 21 |.......| 2016-10-19
3 | 51 | 291 | 19 |.......| 2016-10-20
4 | 79 | 176 | 13 |.......| 2016-10-21
call_center_adsl - TABLE NAME
id | sanal_gomdol | uilchilgee_lavlagaa | shine_serg_zahialga |.......| date
1 | 64 | 264 | 2 |.......| 2016-10-18
2 | 79 | 301 | 7 |.......| 2016-10-19
3 | 53 | 197 | 3 |.......| 2016-10-20
4 | 37 | 239 | 5 |.......| 2016-10-21
call_center_catv - TABLE NAME
id | sanal_gomdol | uilchilgee_lavlagaa | shine_serg_zahialga |.......| date
1 | 8 | 3 | 11 |.......| 2016-10-18
2 | 1 | 9 | 27 |.......| 2016-10-19
3 | 9 | 12 | 19 |.......| 2016-10-20
4 | 5 | 22 | 33 |.......| 2016-10-21
Here is my DB diagram:
There is input form. From that form get into tables.
I want to have one table like that
call_center - TABLE NAME
id | sanal_gomdol | uilchilgee_lavlagaa | shine_serg_zahialga |.......| date with 3 outputs from all column like that. /date is unique/
By ReDesign my DB. How to do this please help me. Sorry My english is bad.
Create your_new_tableName
(Column1 Datatype...Column n Datatype)
Go;
Insert your_new_tableName
FROM
(
Select * from call_center_telephone
UNION ALL
Select * from call_center_adsl
UNION ALL
Select * from call_center_catv
)
Go;
Between if you want to preserve all three tables and want to merge them only for the sake of displaying it , you can think of having a view or temporary table , which combines all the data.
select top 2 *
into new_table_name
from call_center_telephone
union all
select top 2 *
from call_center_adsl
union all
select *
from call_center_catv

MYSQL/Query: How to make table rows into column

I have 3 tables tbl_contestant , tbl_criteria and tbl_judges. And then i have 1 more table combined this 3 table as my result, tbl_score.
tbl_criteria
------------------------
crit_id | criteria_name
16 | sports
tbl_judges
------------------------
judge_id | judge_name
61 | first
62 | second
63 | third
tbl_cotestant
--------------------------------------
con_id | contestant_number | contestant_name |
1 | 1 | john |
2 | 2 | sy |
3 | 3 | Nah |
tbl_score
--------------------------------------------------
score_id | crit_id | judge_id | contestant_number | score
1 | 16 | 61 | 1 | 25
2 | 16 | 61 | 2 | 25
3 | 16 | 61 | 3 | 25
4 | 16 | 62 | 1 | 25
5 | 16 | 62 | 2 | 73
6 | 16 | 62 | 3 | 59
7 | 16 | 63 | 1 | 70
8 | 16 | 63 | 2 | 80
9 | 16 | 63 | 3 | 70
How can i achieve this output, judge_id row turns into column based on crit_id
contestant_number | contestant_name | 16_judge_61 | 16_judge_62 | 16_judge_63 | total
1 | john | 25 | 25 | 70 |
2 | sy | 25 | 73 | 80 |
3 | Nah | 25 | 59 | 70 |
Please correct my query
SELECT DISTINCT(c.contestant_number) , contestant_name , j1.sports as
16_judge_61, j2.sports as 16_judge_62, j3.sports as 16_judge_63 from
tbl_criteria , tbl_score, tbl_contestant c
LEFT JOIN tbl_ // <-- i have no idea how start from here joining those 4 tables together
You could use CASE WHEN to solve this.
SELECT
s.contestant_number,
c.contestant_name,
SUM(CASE WHEN s.crit_id='16' AND s.judge_id='61' THEN s.score END) as 16_judge_61,
SUM(CASE WHEN s.crit_id='16' AND s.judge_id='62' THEN s.score END) as 16_judge_62,
SUM(CASE WHEN s.crit_id='16' AND s.judge_id='63' THEN s.score END) as 16_judge_63,
SUM(s.score) as Total
FROM tbl_score s
INNER JOIN tbl_contestant c ON s.contestant_number = c.contestant_number
GROUP BY s.contestant_number
see SQL Fiddle http://sqlfiddle.com/#!9/9efa5/1

SQL sub queries - one alias column

course_completions
+-----------------------------------------------+
| id coursemodid userid state timemodified |
+-----------------------------------------------+
| 370 23 2 1 1433582890 |
| 329 24 89 1 1427771915 |
| 333 30 39 1 1428309816 |
| 332 32 39 1 1428303307 |
| 327 33 40 1 1427689703 |
| 328 34 89 1 1427710711 |
| 303 35 41 1 1410258482 |
| 358 36 99 1 1432020067 |
| 365 25 2 1 1433142455 |
| 304 26 69 1 1410717866 |
| 353 37 95 1 1430387005 |
| 416 38 2 1 1438972465 |
| 300 27 70 1 1409824001 |
| 302 29 74 1 1412055704 |
| 297 30 2 1 1409582123 |
| 301 133 41 1 1410255923 |
| 336 133 91 1 1428398435 |
| 364 133 40 1 1433142348 |
| 312 133 85 1 1425863621 |
+-----------------------------------------------+
course_modules
+------------------+
| id course |
+------------------+
| 23 6 |
| 24 6 |
| 25 6 |
| 26 6 |
| 27 6 |
| 28 6 |
| 29 8 |
| 30 8 |
| 31 8 |
| 32 8 |
| 33 8 |
| 34 5 |
| 35 5 |
| 36 5 |
| 37 5 |
| 38 5 |
| 39 9 |
| 40 9 |
| 41 9 |
+------------------+
course_mod_settings
+--------------------------------------+
|id course modinstance |
+--------------------------------------+
| 27 8 30 |
| 28 8 31 |
| 29 8 32 |
| 30 8 33 |
| 31 6 23 |
| 32 6 24 |
| 33 6 25 |
| 34 6 26 |
| 35 6 27 |
| 36 6 28 |
| 37 9 39 |
| 38 9 40 |
| 39 9 41 |
+--------------------------------------+
I was trying about to frame two sub queries in one SQL statement like I want the count of 'criteria mod settings' table values in one column and count of 'course_completions' table values in one column for a particular user along with course.
There shouldn't be relation between count(cms.id) and count(cc.id) except course id, because count(cms.id) is the count of user modules and count(cc.id) is the settings count set by default.
OUTPUT:
COURSE USERID count(cms.id) count(cc.id)
6 89 3 6
6 39 7 6
6 40 5 6
8 69 3 4
8 2 0 4
8 95 4 4
COURSE : getting courseid
USERID : getting userid
count(cms.id) : getting the count of user completed modules.
count(cc.id) : getting the count of settings (ex: For course 6, settings count has 6 and for course 4, settings count has 3.
SELECT cm.course
,cc.userid
,count(cc.coursemodid) AS usermodules
,(
SELECT count(ccc.id)
FROM course_mod_settings cms
INNER JOIN course_modules cm ON cms.course = cm.course
) AS modsettings
FROM course_completions cc
INNER JOIN course_modules cm ON cc.coursemodid = cm.id
WHERE cc.STATE = 1
GROUP BY cm.cours
,cc.userid
I have read your comment above.
Have you tried something like this?
I didn't test the query, it's just a thought.
I might be wrong.
SELECT cms.course AS COURSE, cc.userid AS USERID, COUNT(cms.id), COUNT(cc.id) FROM
course_completions AS cc
INNER JOIN course_modules AS cm ON cc.coursemodid = cm.id
INNER JOIN course_mod_settings AS cms cm.course = cms.course
WHERE cc.state = 1
GROUP BY cm.course, cc.userid

Select all combinations

It’s pretty difficult to explain what I try to accomplish with just word. So I tried to make an example which reflects my real project. I have a group of people (Peter, Aaron, Mark, Alicia, Cleo and Mike) who play games (A, B and C) against each other. What I would like to do with my query is to get all combination who played against each other for the particular games.
So for example:
Peter played one A game vs Aaron he won in 3 rounds - > Peter - Aaron - A - 1 - 5 - 1
Mike played two C games vs Cleo and lost twice, in 3 and 4 rounds ->
Mike - Cleo - C - 0 - 3.5 - 2
Example table games:
id playRound player game win rounds
1 1 Peter A 1 5
2 1 Aaron A 0 5
3 1 Alicia B 0 3
4 1 Mark B 1 3
5 1 Mike C 0 4
6 1 Cleo C 1 4
7 2 Alicia A 1 5
8 2 Mark A 0 5
9 2 Peter B 1 3
10 2 Mark B 0 3
11 2 Mike C 0 3
12 2 Cleo C 1 3
13 3 Alicia A 0 4
14 3 Mark A 1 4
15 3 Peter B 0 4
16 3 Aaron B 1 4
17 3 Alicia C 1 5
18 3 Cleo C 0 5
End result:
playerONE playerTwo game avgWinRateOne avgRounds Number
Peter Aaron A 1 5 1
Peter Aaron B 1 3 1
Peter Mark B 0 4 1
Alicia Mark A 0.5 4.5 2
Alicia Mark B 0 3 1
Alicia Cleo C 1 5 1
Mike Cleo C 0 3.5 2
I'm fiddling with this but I Have no idea what im doing
SELECT *
FROM (
SELECT *
FROM `testtable`
WHERE `game` = 'A'
GROUP BY `gameId` , `game` , `player`
)tmp1, (
SELECT *
FROM `testtable`
WHERE `game` = 'A'
GROUP BY `gameId` , `game` , `player`
)tmp2
I think maybe you want this query:
select p1.player, p2.player, p1.game, avg(p1.win), avg(p1.rounds), count(*)
from games p1
inner join games p2
on p1.playround = p2.playround
and p1.game = p2.game
and p1.player != p2.player
group by p1.player, p2.player, p1.game;
It's a relatively basic join, where we associate the games table with itself, to get our game pairings, we can them group by those pairings, in order to use aggregate functions to calculate the averages, and number of games played.
Running it on your sample data gives this output:
mysql> select p1.player, p2.player, p1.game, avg(p1.win), avg(p1.rounds), count(*)
-> from games p1
-> inner join games p2
-> on p1.playround = p2.playround
-> and p1.game = p2.game
-> and p1.player != p2.player
-> group by p1.player, p2.player, p1.game;
+--------+--------+------+-------------+----------------+----------+
| player | player | game | avg(p1.win) | avg(p1.rounds) | count(*) |
+--------+--------+------+-------------+----------------+----------+
| Aaron | Peter | A | 0.0000 | 5.0000 | 1 |
| Aaron | Peter | B | 1.0000 | 4.0000 | 1 |
| Alicia | Cleo | C | 1.0000 | 5.0000 | 1 |
| Alicia | Mark | A | 0.5000 | 4.5000 | 2 |
| Alicia | Mark | B | 0.0000 | 3.0000 | 1 |
| Cleo | Alicia | C | 0.0000 | 5.0000 | 1 |
| Cleo | Mike | C | 1.0000 | 3.5000 | 2 |
| Mark | Alicia | A | 0.5000 | 4.5000 | 2 |
| Mark | Alicia | B | 1.0000 | 3.0000 | 1 |
| Mark | Peter | B | 0.0000 | 3.0000 | 1 |
| Mike | Cleo | C | 0.0000 | 3.5000 | 2 |
| Peter | Aaron | A | 1.0000 | 5.0000 | 1 |
| Peter | Aaron | B | 0.0000 | 4.0000 | 1 |
| Peter | Mark | B | 1.0000 | 3.0000 | 1 |
+--------+--------+------+-------------+----------------+----------+
14 rows in set (0.00 sec)