一个表中的多个排名

时间:2010-07-01 23:29:57

标签: sql mysql

我需要以下内容,任何人都可以帮助我这样做。

Rank   Cust_Type   Cust_Name   Revenue
1      Top         A           10000
2      Top         B           9000
3      Top         C           8000
1      Bottom      X           5000
2      Bottom      Y           6000
3      Bottom      Z           7000

我需要Top和Bottom Cust_Type的单独排名,所有这些都在MySQL中。

6 个答案:

答案 0 :(得分:24)

这有点棘手。您可能希望使用变量,例如以下示例:

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;

(SELECT @curRow := 0, @curType := '') r部分允许变量初始化,而无需单独的SET命令。

测试用例:

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);

结果:

+------+-----------+-----------+---------+
| 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)

另一个测试案例:

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);

结果:

+------+-----------+-----------+---------+
| 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)

显然,您可以按升序排序cust_type而不是降序。我在原始测试用例中使用降序只是Top之前的Bottom

答案 1 :(得分:3)

我发现使用CASE@curRow@curType的解决方案存在问题。它取决于MySQL用于处理查询的执行计划。例如,如果您向查询添加联接,则会显示它。然后无法保证将正确计算排名。

对答案稍作修改:

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);

如果我只查询sales表,我会按正确顺序获得排名,但如果我加入cust_type表,则排名值不再正确

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;

结果:

+------+-----------+-----------+---------+
| 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正在将初始查询运行到临时表中,然后ORDER BY正在针对临时表执行 已经计算了等级。

答案 2 :(得分:3)

这与托马斯的回答类似,但有点简单:

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;

我不确定性能与Daniel的解决方案相比如何,特别是在非常大的数据集上,或者您必须使用复杂的连接。

答案 3 :(得分:1)

不清楚的是如何对项目进行排名(我假设为收入)或者您是否只提取了一定数量的值(例如前3位和后3位),所以我假设您想要所有值。鉴于这些假设,

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'

如果您尝试在单个非联合查询中执行此操作,则可以执行以下操作:

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

答案 4 :(得分:0)

通过保持销售收入排序和订单分开,这对我有用。

getScene()

答案 5 :(得分:0)

对于JOIN表问题,我找到了解决方法。

我创建了一个临时表,这样MySQL可以保持我要排名的值的顺序。

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:我尝试过创建视图,但是也无法正常工作。