Hello can you help me get the rank with custom ties?
i have a table of Scores stores all the scores given by the judges.
+----+----------+-------------+--------+
| Id | judge_id |performer_id | score |
+----+----------+-------------+--------+
| 1 | 1 | 1 | 98 |
| 2 | 1 | 2 | 98 |
| 3 | 1 | 3 | 94 |
| 4 | 1 | 4 | 96 |
| 5 | 2 | 1 | 93 |
| 6 | 2 | 2 | 80 |
+----+----------+-------------+--------+
heres what the code i have searched.
SELECT
id
, judge_id
, performer_id
, score
, FIND_IN_SET(
score
, (SELECT
GROUP_CONCAT(DISTINCT score ORDER BY score DESC)
FROM
scores
WHERE
judge_id = 1
)
) AS rank
FROM
scores
WHERE
judge_id = 1
ORDER BY rank ASC
and the output of this is:
+----+----------+-------------+--------+------+
| Id | judge_id |performer_id | score | rank |
+----+----------+-------------+--------+------+
| 1 | 1 | 1 | 98 | 1 |
| 2 | 1 | 2 | 98 | 1 |
| 3 | 1 | 4 | 96 | 3 |
| 4 | 1 | 3 | 94 | 4 |
+----+----------+-------------+--------+------+
it is working but the output is not what i want.
i want to get the ranking and ties like this.
+----+----------+-------------+--------+------+
| Id | judge_id |performer_id | score | rank |
+----+----------+-------------+--------+------+
| 1 | 1 | 1 | 98 | 1.5 |
| 2 | 1 | 2 | 98 | 1.5 |
| 3 | 1 | 4 | 96 | 3 |
| 4 | 1 | 3 | 94 | 4 |
+----+----------+-------------+--------+------+
where get all the rank of the tie then divide it by how many performer ties in the rank.
ex.
performer 1 score 98 rank 1
performer 2 score 98 rank 1
suppose that performer 2 should get rank 2
i want to compute it like
1+2 = 3 then divide it by 2 since 2 performers are tie in rank 1
1=2 = 3 / 2
answer is 1.5
im sorry for my english
but please can any one help me? im stuck at this problem.
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(performer_id SERIAL PRIMARY KEY
,score INT NOT NULL
);
INSERT INTO my_table VALUES
(1,98),
(2,98),
(3,94),
(4,96);
SELECT x.*
, y.rank
FROM my_table x
JOIN
( SELECT score
, SUM(i)/COUNT(*) rank
FROM
( SELECT score
, #i:=#i+1 i
FROM my_table x
ORDER
BY score DESC
, performer_id
) a
JOIN
( SELECT #i:=0 ) vars
GROUP
BY score
) y
ON y.score = x.score;
+--------------+-------+--------+
| performer_id | score | rank |
+--------------+-------+--------+
| 1 | 98 | 1.5000 |
| 2 | 98 | 1.5000 |
| 3 | 94 | 4.0000 |
| 4 | 96 | 3.0000 |
+--------------+-------+--------+
Note: For newer versions of MySQL, you would use Windowing functions/CTE (I'm not really sure of the correct terminology). I've written this for older versions, although MySQL can have issues when initializing variables this way. If that's a problem, here's another (slightly 'hackier' - and theoretically incorrect, but practically fine) way of initialising the variable...
SELECT x.*
, y.rank
FROM my_table x
JOIN
( SELECT score
, SUM(i)/COUNT(*) rank
FROM
( SELECT score
, #j:=#j+1 i
FROM my_table x
JOIN ( SELECT #j:=0 ) vars
ORDER
BY score DESC
, performer_id
) a
GROUP
BY score
) y
ON y.score = x.score;
+--------------+-------+------+
| performer_id | score | rank |
+--------------+-------+------+
| 1 | 98 | 1.5 |
| 2 | 98 | 1.5 |
| 3 | 94 | 4 |
| 4 | 96 | 3 |
+--------------+-------+------+
Related
I have a MySQL table like bellow
id | roll | exam_id | course_id | marks | status
----------------------------------------------------------
1 | 01001 | 1 | 1 | 56 | 1
2 | 01002 | 1 | 1 | 68 | 1
3 | 01003 | 1 | 1 | 55 | 1
4 | 01004 | 1 | 1 | 67 | 1
5 | 01001 | 1 | 2 | 54 | 1
6 | 01002 | 1 | 2 | 59 | 1
7 | 01003 | 1 | 2 | 62 | 1
8 | 01004 | 1 | 2 | 63 | 1
9 | 01001 | 2 | 3 | 61 | 1
10 | 01002 | 2 | 3 | 48 | 1
11 | 01003 | 2 | 3 | 22 | 1
12 | 01004 | 2 | 3 | 39 | 1
Now I want to have all the row with exam_id = 1
SELECT * FROM result WHERE exam_id=1 ORDER BY course_id
After that I need to display this table inside HTML after grouping it by roll means one row for each roll that have row-span according to the course number of the result table
Roll | course_id | marks
-----------------------------
01001 | 1 | 56
| 2 | 68
-----------------------------
01002 | 1 | 55
| 2 | 67
-----------------------------
01003 | 1 | 55
| 2 | 62
-----------------------------
01004 | 1 | 67
| 2 | 63
I'm using Codeigniter framework to doing this project. Any suggestions about how I can I do this?
Thank you in advance.
[EDIT]
Current SQL I'm using to do this:
SELECT * FROM `exam_result` JOIN `course` ON `course`.`course_tab_id`=`exam_result`.`result_course` WHERE `exam_id` = '1' AND `result_status` = 1 GROUP BY `exam_result`.`exam_roll`, `course`.`course_tab_id` ORDER BY `exam_result`.`exam_roll` ASC, `course`.`course_id` ASC
Try this but the difference in this table on my first query is that the course title will be included on a single table, I have set the marks as AVG due to so if ever you have duplicate data you will see their average result for the course and roll of a school or whatever you use this structure for
SELECT roll, exam_id, a.course_id, marks, status, course_title, course_credit
FROM
(SELECT roll, exam_id, course_id, AVE(marks) as marks, status FROM result) as a
LEFT JOIN
(SELECT course_id, course_title, course_credit FROM course) as b
ON
a.course_id = b.course_id
WHERE exam_id = '1' -- you can remove this if you wanted all exam appear on your list
GROUP BY roll, exam_id, a.course_id, marks, status, course_title, course_credit
ORDER BY roll, course_id, marks
Try this sql query
SELECT Roll, course_id, AVG(marks)
FROM result WHERE exam='1'
GROUP BY Roll, course_id
ORDER BY roll, course_id, marks
i have 3 table user, products and sales
user
UID | NAME
1 | agent1
2 | agent2
3 | agent3
4 | agent4
5 | agent5
products
PID | PNAME
1 | P1
2 | P2
3 | P3
sales
SID | UID | PID | SALES_CREATED
1 | 3 | 1 | 2013-07-13 01:15:04
2 | 1 | 1 | 2013-07-13 01:25:34
3 | 3 | 1 | 2013-07-13 02:01:34
4 | 3 | 1 | 2013-07-13 02:45:34
5 | 5 | 1 | 2013-07-13 02:56:34
6 | 5 | 1 | 2013-07-13 03:21:34
7 | 2 | 3 | 2013-07-13 03:38:34
8 | 3 | 2 | 2013-07-13 03:51:34
9 | 4 | 2 | 2013-07-13 04:25:34
10 | 3 | 1 | 2013-07-13 04:45:04
11 | 1 | 3 | 2013-07-13 04:55:34
12 | 2 | 2 | 2013-07-13 05:01:34
13 | 1 | 3 | 2013-07-13 05:15:34
14 | 5 | 3 | 2013-07-13 05:36:34
15 | 5 | 3 | 2013-07-13 06:21:34
where the results of sales per agent is: (sort by user UID)
user | P1 | P2 | P3 |
agent1 | 1 | 0 | 2 |
agent2 | 0 | 1 | 1 |
agent3 | 4 | 1 | 0 |
agent4 | 0 | 1 | 0 |
agent5 | 2 | 0 | 2 |
now, i want a result to sort user that has a greatest sale by P1 and a minimum of 3 result only, and the result will be like this
user | P1 | P2 | P3 |
agent3 | 4 | 1 | 0 |
agent5 | 2 | 0 | 2 |
agent1 | 1 | 0 | 2 |
can you guys give me a best mysql_query to show that result?
Oh, something like this...
SELECT uid
, SUM(pid=1) p1
, SUM(pid=2) p2
, SUM(pid=3) p3
FROM sales
GROUP
BY uid
ORDER
BY p1 DESC
, p2 DESC
, p3 DESC;
+-----+------+------+------+
| uid | p1 | p2 | p3 |
+-----+------+------+------+
| 3 | 4 | 1 | 0 |
| 5 | 2 | 0 | 2 |
| 1 | 1 | 0 | 2 |
| 2 | 0 | 1 | 1 |
| 4 | 0 | 1 | 0 |
+-----+------+------+------+
If handling the display logic at the application level, then your query can be simplified to something more scalable, like this...
SELECT uid
, pid
, COUNT(*)
FROM sales
GROUP
BY uid,pid;
I am only answering because (1) I am bored, and (2) I wanted a brain teaser exercise. I typically do not like to answer when people post and run (don't respond to anyone's comments/posts), or provide what they have tried (no query).
If you want better/more help on SO in the future, it would be beneficial to post the query that you have tried, and when using complex/diverse data like this to create a sqlfiddle - http://sqlfiddle.com/
So with that caveat, here is how you can get your desired results
SELECT
user.NAME as user,
SUM(PID=1) as P1,
SUM(PID=2) P2,
SUM(PID=3) P3
FROM
sales
LEFT JOIN
user
ON
user.UID = sales.UID
GROUP BY
sales.UID
HAVING
P1+P2+P3 >= 3
ORDER BY
P1 DESC, P2 DESC, P3 DESC;
and here is the sqlfiddle example that shows it in action - http://sqlfiddle.com/#!2/d3405/8
I have two tables:
Students Student_Grades
V------------------------V
+----+------+ +----+------------+---------+-------+
| id | name | | id | student_id | subject | grade |
+----+------+ +----+------------+---------+-------+
| 0 | Dave | | 0 | 0 | Math | 100 |
+----+------+ +----+------------+---------+-------+
| 1 | John | | 1 | 0 | Chem | 90 |
+----+------+ +----+------------+---------+-------+
| 2 | Kate | | 2 | 0 | CompSCI | 95 |
+----+------+ +----+------------+---------+-------+
| 3 | Mimi | | 3 | 1 | ELA | 98 |
+----+------+ +----+------------+---------+-------+
| 4 | 2 | Biology | 92 |
+----+------------+---------+-------+
| 5 | 2 | Chem | 94 |
+----+------------+---------+-------+
| 6 | 2 | Math | 98 |
+----+------------+---------+-------+
| 7 | 3 | Math | 100 |
+----+------------+---------+-------+
I would like to select all subjects and grades from a random student that is enrolled in more than three subjects. (Either Dave or Kate)
Students John and Mimi would not be even considered because they are not enrolled in three subjects.
I know I can achieve this with PHP but I would like this to be done with one query to the database.
SELECT * FROM Students t JOIN (SELECT CEIL(MAX(ID)*RAND()) AS ID FROM Students) AS x ON t.ID >= x.ID LIMIT 1
With the above query, I have selected a random student, with that I can go in and check if they have three subjects with SELECT count(subjects) FROM Students WHERE id=random_id.
If the count returned is below three, then I throw away the results and run the first query again.
How would I attempt this in one query?
This is tested and working:
SELECT *
FROM Students s
JOIN (
SELECT student_id
FROM Student_Grades
GROUP BY student_id
HAVING COUNT(*) >= 3
ORDER BY RAND()
LIMIT 1
) rs
ON rs.student_id = s.id
JOIN
Student_Grades sg
ON sg.student_id = s.id
Here's the SQL Fiddle: http://sqlfiddle.com/#!2/e5b5b/1
I have a query that needs a custom sorting, trimmed down to the bare minimums something like:
SELECT u.*, p.*, p.id as product_id
FROM users u, products p
WHERE u.id = p.user_id
ORDER BY product_id DESC
And I get returned a set of rows like:
UserID ProductID
2 5
2 4
3 3
1 2
1 1
But I want it to actually sort SOMETHING like this (so no 2 UserIDs are adjacent to eachother):
UserID ProductID
1 2
2 4
3 3
2 5
1 1
Is this even possible with MySQL, or do I need some PHP magic?
A canonical way of solving this problem is by enumerating the duplicate rows and then ordering by that value:
select t.*
from (SELECT u.*, p.*, p.id as product_id,
row_number() over (partition by u.id order by (select NULL)) as seqnum
FROM users u join
products p
on u.id = p.user_id
) t
order by seqnum, id;
This will work, as long as no one user has a really long sequence (as in your example).
There is no "always-works" solution, because it is easy to come up with a situation where your goal is not possible.
Here fetch your sorted results into an array. Then do something like this.
$records = $res->fetchAll();
$count = count($records);
$records = array_chunk($records, ceil(count($records)/2);
$unsorted = array();
for($x = 0; $x < $count; $x++){
$unsorted[] = $records[$x%2][floor($x/2)];
}
Consider the following...
CREATE TABLE sortable(id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,player_id INT NOT NULL);
INSERT INTO sortable(player_id) VALUES (1),(1),(2),(3),(4),(3),(3),(2),(1),(2),(4),(4);
SELECT * FROM sortable;
+----+-----------+
| id | player_id |
+----+-----------+
| 1 | 1 |
| 2 | 1 |
| 3 | 2 |
| 4 | 3 |
| 5 | 4 |
| 6 | 3 |
| 7 | 3 |
| 8 | 2 |
| 9 | 1 |
| 10 | 2 |
| 11 | 4 |
| 12 | 4 |
+----+-----------+
SELECT x.*,COUNT(*) rank FROM sortable x JOIn sortable y ON y.player_id = x.player_id AND y.id <= x.id GROUP BY x.id ORDER BY player_id,rank;
+----+-----------+------+
| id | player_id | rank |
+----+-----------+------+
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 9 | 1 | 3 |
| 3 | 2 | 1 |
| 8 | 2 | 2 |
| 10 | 2 | 3 |
| 4 | 3 | 1 |
| 6 | 3 | 2 |
| 7 | 3 | 3 |
| 5 | 4 | 1 |
| 11 | 4 | 2 |
| 12 | 4 | 3 |
+----+-----------+------+
SELECT x.*,COUNT(*) rank FROM sortable x JOIn sortable y ON y.player_id = x.player_id AND y.id <= x.id GROUP BY x.id ORDER BY rank;
+----+-----------+------+
| id | player_id | rank |
+----+-----------+------+
| 1 | 1 | 1 |
| 3 | 2 | 1 |
| 4 | 3 | 1 |
| 5 | 4 | 1 |
| 2 | 1 | 2 |
| 8 | 2 | 2 |
| 6 | 3 | 2 |
| 11 | 4 | 2 |
| 9 | 1 | 3 |
| 10 | 2 | 3 |
| 7 | 3 | 3 |
| 12 | 4 | 3 |
+----+-----------+------+
So if your problem is just that you dont want two records with same id should not come next to each other wha I can think simplest is use
SELECT u.*, p.*, p.id as product_id
FROM users u, products p
WHERE u.id = p.user_id
ORDER BY user_id%2 DESC
Or you can even use other number than 2 to meet any certain order you want....
i have this table called bag:
+--------+----------+---------+----------+
| bag_id | chara_id | item_id | item_qty |
+--------+----------+---------+----------+
| 1 | 1 | 2 | 22 |
| 2 | 1 | 1 | 55 |
| 3 | 3 | 1 | 2 |
| 6 | 3 | 4 | 2 |
| 7 | 4 | 4 | 2 |
| 8 | 5 | 4 | 2 |
| 9 | 6 | 4 | 2 |
| 10 | 1 | 5 | 1 |
| 11 | 1 | 2 | 1 |
| 12 | 1 | 2 | 1 |
| 13 | 1 | 2 | 1 |
| 14 | 1 | 8 | 1 |
| 15 | 1 | 6 | 1 |
| 16 | 1 | 8 | 1 |
| 17 | 1 | 6 | 1 |
+--------+----------+---------+----------+
the relationship goes as 1 chara = many item
now i dont want 1 chara = many duplicated item.
how can i make a query that delete's the duplicated values?
like chara_id: 1 has 3 duplicated item_id: 2
i want to delete the other 2.
Not the best way to do it. But the below should definetly work:
Delete from Bag
where bag_id
not in (
select min(bag_id) from bag a,
(select chara_id, item_id
from bag group by chara_id, item_id
having count(*) > 1) b
where a.chara_id = b.chara_id and a.item_id = b.item_id
UNION
select bag_id from bag a,
(select chara_id, item_id
from bag group by chara_id, item_id
having count(*) = 1) b
where a.chara_id = b.chara_id and a.item_id = b.item_id
)
You can simply join table bag with a subquery which gets the minimum bag_id for every combination of chara_ID and item_ID. Records that have null values on any fields on the subquery are the records that will be deleted.
DELETE a
FROM bag a
LEFT JOIN
(
SELECT chara_ID, item_ID, MIN(bag_ID) min_ID
FROM bag
GROUP BY chara_ID, item_ID
) b ON a.bag_ID = b.min_ID AND
a.chara_ID = b.chara_ID AND
a.item_ID = b.item_ID
WHERE b.min_ID IS NULL
SQLFiddle Demo