Несколько рангов в одной таблице
Мне нужно следующее: может ли кто-нибудь помочь мне сделать это.
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.
Ответы
Ответ 1
Это немного сложно. Вы можете использовать переменные, например, в следующем примере:
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
в исходном тестовом примере.
Ответ 2
Я нашел проблему с решением, используя 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
выполняется против таблицы temp после. Ранг уже был вычислен.
Ответ 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, особенно на очень больших наборах данных, или если вам нужно использовать сложные объединения.
Ответ 4
Что не совсем ясно, так это то, как элементы должны быть ранжированы (я предполагал Revenue) или вы только вытаскиваете определенное количество значений (например, верхние 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