I need the following, Can anyone please help me do it.
Rank Cust_Type Cust_Name Revenue
1 Top A 10000
2 Top B
This is a bit tricky. You may want to use variables, such as in the following example:
SELECT (
CASE cust_type
WHEN @curType
THEN @curRow := @curRow + 1
ELSE @curRow := 1 AND @curType := cust_type END
) + 1 AS rank,
cust_type,
cust_name,
revenue
FROM sales,
(SELECT @curRow := 0, @curType := '') r
ORDER BY cust_type DESC, revenue DESC;
The (SELECT @curRow := 0, @curType := '') r
part allows the variable initialization without requiring a separate SET
command.
Test case:
CREATE TABLE sales (cust_type varchar(10), cust_name varchar(10), revenue int);
INSERT INTO sales VALUES ('Top', 'A', 10000);
INSERT INTO sales VALUES ('Top', 'B', 9000);
INSERT INTO sales VALUES ('Top', 'C', 8000);
INSERT INTO sales VALUES ('Bottom', 'X', 5000);
INSERT INTO sales VALUES ('Bottom', 'Y', 6000);
INSERT INTO sales VALUES ('Bottom', 'Z', 7000);
Result:
+------+-----------+-----------+---------+
| rank | cust_type | cust_name | revenue |
+------+-----------+-----------+---------+
| 1 | Top | A | 10000 |
| 2 | Top | B | 9000 |
| 3 | Top | C | 8000 |
| 1 | Bottom | Z | 7000 |
| 2 | Bottom | Y | 6000 |
| 3 | Bottom | X | 5000 |
+------+-----------+-----------+---------+
6 rows in set (0.00 sec)
Another test case:
CREATE TABLE sales (cust_type varchar(10), cust_name varchar(10), revenue int);
INSERT INTO sales VALUES ('Type X', 'A', 7000);
INSERT INTO sales VALUES ('Type X', 'B', 8000);
INSERT INTO sales VALUES ('Type Y', 'C', 5000);
INSERT INTO sales VALUES ('Type Y', 'D', 6000);
INSERT INTO sales VALUES ('Type Y', 'E', 4000);
INSERT INTO sales VALUES ('Type Z', 'F', 4000);
INSERT INTO sales VALUES ('Type Z', 'G', 3000);
Result:
+------+-----------+-----------+---------+
| rank | cust_type | cust_name | revenue |
+------+-----------+-----------+---------+
| 1 | Type Z | F | 4000 |
| 2 | Type Z | G | 3000 |
| 1 | Type Y | D | 6000 |
| 2 | Type Y | C | 5000 |
| 3 | Type Y | E | 4000 |
| 1 | Type X | B | 8000 |
| 2 | Type X | A | 7000 |
+------+-----------+-----------+---------+
7 rows in set (0.00 sec)
You can obviously order the cust_type
in ascending order instead of descending. I used descending just to have Top
before Bottom
in the original test case.
This is similar to Thomas's answer, but a little simpler:
SELECT (SELECT COUNT(Cust_Type) FROM sales
WHERE Cust_Type = S.Cust_Type AND Revenue >= S.Revenue) AS Rank,
Cust_Type,
Cust_Name,
Revenue
FROM sales AS S
ORDER BY Cust_Type DESC, Rank;
I'm not sure how the performance compares to the Daniel's solution, particularly on very large data sets, or if you have to use complex joins.
What is not exactly clear is how the items should be ranked (I assumed by Revenue) or whether you are only pulling a certain number of values (e.g. the top 3 and the bottom 3) so I assumed you wanted all values. Given those assumptions,
Select Cust_Name, Cust_Type
, (Select Count(*)
From Table As T1
Where T1.Revenue > T.Revenue ) + 1 As Rank
From Table As T
Where Cust_Type = 'Top'
Union All
Select Cust_Name, Cust_Type
, (Select Count(*)
From Table As T1
Where T1.Revenue < T.Revenue ) + 1 As Rank
From Table As T
Where Cust_Type = 'Bottom'
If you were trying to do this in a single non-union query you could do:
Select Cust_Name, Cust_Type
, Case Z.Cust_Type
When 'Top' Then Z.TopRank
Else Z.BottomRank
End As Rank
From (
Select Cust_Name, Cust_Type
, (Select Count(*)
From Table As T1
Where T1.Revenue > T.Revenue ) + 1 As TopRank
, (Select Count(*)
From Table As T1
Where T1.Revenue < T.Revenue ) + 1 As BottomRank
From Table As T
) As Z
This works for me by keeping ranking of sales revenue and ordering separate .
SELECT
(Select count(s1.revenue)+1 from sales s1 where s.cust_type_id = s1.cust_type_id and s1.revenue > s.revenue)
As rank,
t.type_name,
s.cust_name,
s.revenue
FROM sales s LEFT JOIN
cust_type t USING(cust_type_id)
Group by t.type_name,s.cust_name,s.revenue DESC order by s.revenue DESC;
For JOIN tables problem, I found a solution.
I create a temporary table, this way MySQL maintain the order of value that I want to rank.
DROP TEMPORARY TABLE IF EXISTS tmp_mytable;
CREATE TEMPORARY TABLE tmp_mytable ENGINE = MEMORY
SELECT mytable.id AS id,
mytable.login AS Login,
cliente.myrank_id AS id_myrank,
mytable.rankvalue AS rankvalue
FROM mytable
INNER JOIN myjoin ON (mytable.id_myjoin = myjoin.id)
ORDER BY 3, 4 DESC;
SELECT id, login, IFNULL(id_myrank, 0) AS id_myrank, rankvalue,
@rank := IF(@prev_born = IFNULL(id_myrank, 0), @rank + 1, 1) AS ranking,
@prev_Born := IFNULL(id_myrank, 0) AS fake_field
FROM tmp_mytable, (select @prev_born := 0, @rank := 0) r
-- HAVING ranking < 20;
*PS: I tried with View creation, but insn't work too.
I found a problem with the solution using CASE
, @curRow
, and @curType
. It depends on the execution plan MySQL uses to process the query. For example, it shows up if you add a join to the query. Then there is no guarantee that the rank is going to be computed correctly.
Making a slight change to the answer:
CREATE TABLE sales (cust_type_id int, cust_name varchar(10), revenue int);
CREATE TABLE cust_type (cust_type_id int, type_name varchar(10));
INSERT INTO cust_type VALUES (1, 'Bottom');
INSERT INTO cust_type VALUES (2, 'Top');
INSERT INTO sales VALUES (2, 'A', 10000);
INSERT INTO sales VALUES (2, 'B', 9000);
INSERT INTO sales VALUES (2, 'C', 8000);
INSERT INTO sales VALUES (1, 'X', 5000);
INSERT INTO sales VALUES (1, 'Y', 6000);
INSERT INTO sales VALUES (1, 'Z', 7000);
If I query only the sales
table I get the rank in the correct order, but if I join to the cust_type
table the rank values are no longer correct
SELECT (
CASE s.cust_type_id
WHEN @curType
THEN @curRow := @curRow + 1
ELSE @curRow := 1 AND @curType := s.cust_type_id END
) AS rank,
t.type_name,
s.cust_name,
s.revenue
FROM sales s,
cust_type t,
(SELECT @curRow := 0, @curType := 0) r
WHERE s.cust_type_id = t.cust_type_id
ORDER BY t.type_name DESC, s.revenue DESC;
Result:
+------+-----------+-----------+---------+
| rank | type_name | cust_name | revenue |
+------+-----------+-----------+---------+
| 1 | Top | A | 10000 |
| 2 | Top | B | 9000 |
| 3 | Top | C | 8000 |
| 3 | Bottom | Z | 7000 |
| 2 | Bottom | Y | 6000 |
| 1 | Bottom | X | 5000 |
+------+-----------+-----------+---------+
MySQL is running the initial query into a temp table and then the ORDER BY
is executing against the temp table after a rank was already computed.