Múltiples ranges en una table

Necesito lo siguiente, ¿alguien puede ayudarme a hacerlo?

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 

Necesito ranges separados para Top y Bottom Cust_Type y todo esto está en MySQL.

Esto es un poco complicado. Es posible que desee utilizar variables, como en el siguiente ejemplo:

 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; 

La parte (SELECT @curRow := 0, @curType := '') r permite la initialization de la variable sin requerir un command SET separado.

Caso de testing:

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

Resultado:

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

Otro caso de testing:

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

Resultado:

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

Obviamente puedes orderar el cust_type en order ascendente en lugar de descender. Solía ​​descender solo para tener Top antes de Bottom en el caso de testing original.

Encontré un problema con la solución usando CASE , @curRow y @curType . Depende del plan de ejecución que MySQL utiliza para procesar la consulta. Por ejemplo, aparece si agrega un join a la consulta. Entonces no hay garantía de que el range vaya a ser calculado correctamente.

Haciendo un ligero cambio en la respuesta:

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

Si consulto solo la tabla de sales obtengo el range en el order correcto, pero si me uno a la tabla cust_type los valores de range ya no son correctos

 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; 

Resultado:

 +------+-----------+-----------+---------+ | 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 está ejecutando la consulta inicial en una tabla temporal y luego el ORDER BY está ejecutando contra la tabla temporal después de que ya se calculó un range.

Esto es similar a la respuesta de Thomas, pero un poco más simple:

 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; 

No estoy seguro de cómo se compara el performance con la solución de Daniel, particularmente en sets de datos muy grandes, o si tiene que usar combinaciones complejas.

Lo que no está exactamente claro es cómo deben clasificarse los artículos (supongo por Ingresos) o si solo está obteniendo un cierto número de valores (por ejemplo, los 3 superiores y los 3 inferiores), así que asumí que quería todos los valores. Dados esos supuestos,

 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' 

Si estuviera tratando de hacer esto en una única consulta no sindical, podría hacer lo siguiente:

 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