Несколько рангов в одном столе - PullRequest
13 голосов
/ 02 июля 2010

Мне нужно следующее, Может кто-нибудь, пожалуйста, помогите мне сделать это.

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 ]

24 голосов
/ 02 июля 2010

Это немного сложно. Вы можете использовать переменные, например, в следующем примере:

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 в исходном тестовом примере.

3 голосов
/ 13 сентября 2013

Это похоже на ответ Томаса, но немного проще:

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 голосов
/ 24 июля 2013

Я нашел проблему с решением, используя 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 выполняется для временной таблицы после ранг уже был вычислен.

1 голос
/ 02 июля 2010

Что не совсем ясно, так это то, как элементы должны быть ранжированы (я предполагаю, что выручка) или вы тянете только определенное количество значений (например, верхние 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
0 голосов
/ 18 октября 2018

Для проблемы таблиц 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: Я пытался с созданием View, но тоже не работает.

0 голосов
/ 18 февраля 2018

Это работает для меня, сохраняя рейтинг выручки от продаж и отделяя заказ.

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;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...