Volver a la lista de artículos Artículos
Lectura de 9 minutos

Cómo utilizar la función SQL RANK OVER (PARTITION BY)

Clasificar datos en SQL es pan comido si sabe cómo utilizar RANK() para clasificar sobre una partición. Este artículo muestra cómo hacerlo y en qué se diferencia RANK() de DENSE_RANK() y ROW_NUMBER().

Si trabaja con SQL en un entorno profesional, habrá tenido que clasificar datos al menos una vez. Piense en clasificar meses/años/trimestres por ingresos o costes, los productos más vendidos, los posts más vistos o las canciones más escuchadas, los empleados por salario, las sucursales más rentables, etc. O clasificar libros por sus ventas.

O clasificar libros por sus ventas.

El caso es que a menudo hay que clasificar los datos dentro de una categoría determinada, o partición, como la llamamos nosotros. El SQL RANK OVER (PARTITION BY) entra en juego.

Este es un ejemplo típico de funciones ventana en SQL. Para una explicación más detallada de las funciones ventana, nuestro curso de funciones ventana es el mejor. A través de 218 ejercicios interactivos, aprenderá más sobre PARTITION BY y otras cláusulas de funciones ventana, como ORDER BY, ROWS y RANGE. Estas funciones se utilizan no sólo en la clasificación, sino también en las funciones ventana de agregación y análisis.

Volvamos a las ventas de libros. Eche un vistazo a esta tabla, con el código para crearla aquí. ¿Cómo clasificamos las ventas de libros dentro de cada idioma?

idtitleauthororiginal_languagesalesclassify_under
1The HobbitJ. R. R. TolkienEnglish100Fantasy
2Watership DownRichard AdamsEnglish50Fantasy
3Harry Potter and the Philosopher's StoneJ. K. RowlingEnglish120Fantasy
4The PlagueAlbert CamusFrench12Classics
5The Divine ComedyDante AlighieriItalian12Poetry
6War and PeaceLeo TolstoyRussian36Classics
7Nineteen Eighty-FourGeorge OrwellEnglish30Classics
8Andromeda NebulaIvan YefremovRussian20Science fiction
9The Little PrinceAntoine de Saint-ExupéryFrench200Kids
10The StrangerAlbert CamusFrench10Classics
11The Adventures of PinocchioCarlo CollodiItalian35Kids
12The Name of the RoseUmberto EcoItalian50Classics
13One Hundred Years of SolitudeGabriel García MárquezSpanish50Classics
14Don QuixoteMiguel de CervantesSpanish500Classics
15LolitaVladimir NabokovEnglish50Classics

La solución es sencilla si sabemos que el idioma, en este ejemplo, actúa como algo llamado partición de datos.

SELECT
  original_language,
  title,
  author,
  sales,
  RANK() OVER (PARTITION BY original_language ORDER BY sales DESC)
    AS sales_rank
FROM books;

Y aquí está.

original_languagetitleauthorsalessales_rank
EnglishHarry Potter and the Philosopher's StoneJ. K. Rowling1201
EnglishThe HobbitJ. R. R. Tolkien1002
EnglishLolitaVladimir Nabokov503
EnglishWatership DownRichard Adams503
EnglishNineteen Eighty-FourGeorge Orwell305
FrenchThe Little PrinceAntoine de Saint-Exupéry2001
FrenchThe PlagueAlbert Camus122
FrenchThe StrangerAlbert Camus103
ItalianThe Name of the RoseUmberto Eco501
ItalianThe Adventures of PinocchioCarlo Collodi352
ItalianThe Divine ComedyDante Alighieri123
RussianWar and PeaceLeo Tolstoy361
RussianAndromeda NebulaIvan Yefremov202
SpanishDon QuixoteMiguel de Cervantes5001
SpanishOne Hundred Years of SolitudeGabriel García Márquez502

Ahora, vamos a diseccionar este ejemplo.

¿Qué es RANK()?

En términos más simples, RANK() es una función ventana.

Las funciones ventana de SQL son similares a las funciones agregadas en el sentido de que se aplican a un grupo de filas. Una gran diferencia: las funciones ventana mantienen los detalles de las filas individuales, a diferencia de las funciones agregadas con GROUP BY.

¿Qué hace RANK()?

Como su nombre indica, clasifica los datos. Esto la convierte en una función ventana de clasificación, junto con DENSE_RANK() y ROW_NUMBER().

Al utilizar cualquiera de estas funciones de ventana, debe ir acompañada de una cláusula OVER (ORDER BY). La cláusula OVER() es obligatoria para cualquier función ventana. Es lo que convierte una función "normal" en una función ventana.

Para estas funciones de clasificación, el ORDER BY entre paréntesis define el orden en que se realiza la clasificación. Este orden puede ser ascendente o descendente. Tenga en cuenta que no afecta al orden de las filas en el resultado; eso se hace con un ORDER BY al final de la consulta.

Obtenga más información en nuestro artículo sobre la función de ventana RANK().

Cómo funciona RANK() con OVER (ORDER BY)

Utilicemos el conjunto de datos anterior. Tome toda la tabla y ordene los libros por ventas. Veamos qué ocurre.

SELECT
  title,
  author,
  sales,
  RANK() OVER (ORDER BY sales DESC) AS sales_rank
FROM books;

Es igual que la consulta anterior, excepto que en esta no se selecciona el idioma y no se utiliza PARTITION BY. Así, la función RANK() va seguida de OVER(). La cláusula ORDER BY indica a la función que ordene los datos por ventas en orden descendente, es decir, de los libros más vendidos a los menos vendidos. Como se omite la cláusula PARTITION BY, la función ordena toda la tabla.

A continuación se muestran las diez primeras filas del resultado.

titleauthorsalessales_rank
Don QuixoteMiguel de Cervantes5001
The Little PrinceAntoine de Saint-Exupéry2002
Harry Potter and the Philosopher's StoneJ. K. Rowling1203
The HobbitJ. R. R. Tolkien1004
LolitaVladimir Nabokov505
Watership DownRichard Adams505
The Name of the RoseUmberto Eco505
One Hundred Years of SolitudeGabriel García Márquez505
War and PeaceLeo Tolstoy369
The Adventures of PinocchioCarlo Collodi3510

Lo más importante: hay cuatro libros con 50 millones de ejemplares vendidos y todos ocupan el quinto lugar.

Así es como RANK() difiere de las otras dos funciones de ventana de clasificación: da el mismo rango a los valores empatados. Cuando la función alcanza el siguiente valor de ventas (en este caso, 36 millones de ejemplares vendidos), no asigna el siguiente valor de rango consecutivo (6), sino que se lo salta para ajustar el recuento de valores de ventas empatados. Como se ha mencionado, el rango "5" aparece cuatro veces; por lo tanto, el siguiente rango asignado es el nueve.

DENSE_RANK() también clasifica los empates con el mismo rango. Sin embargo, a diferencia de RANK(), no omite valores de clasificación basados en empates. Los mismos datos clasificados con DENSE_RANK() son los siguientes.

titleauthorsalessales_rank
Don QuixoteMiguel de Cervantes5001
The Little PrinceAntoine de Saint-Exupéry2002
Harry Potter and the Philosopher's StoneJ. K. Rowling1203
The HobbitJ. R. R. Tolkien1004
LolitaVladimir Nabokov505
Watership DownRichard Adams505
The Name of the RoseUmberto Eco505
One Hundred Years of SolitudeGabriel García Márquez505
War and PeaceLeo Tolstoy366
The Adventures of PinocchioCarlo Collodi357

Después de varios libros clasificados en quinto lugar, el siguiente lugar es el sexto, no el noveno como en RANK().

¿Qué ocurre con ROW_NUMBER()? No le importan los empates ni los saltos. Sólo ordena las filas secuencialmente. A continuación se muestran las diez primeras filas con ROW_NUMBER().

titleauthorsalessales_rank
Don QuixoteMiguel de Cervantes5001
The Little PrinceAntoine de Saint-Exupéry2002
Harry Potter and the Philosopher's StoneJ. K. Rowling1203
The HobbitJ. R. R. Tolkien1004
LolitaVladimir Nabokov505
Watership DownRichard Adams506
The Name of the RoseUmberto Eco507
One Hundred Years of SolitudeGabriel García Márquez508
War and PeaceLeo Tolstoy369
The Adventures of PinocchioCarlo Collodi3510

Las diferencias entre estas funciones se explican en la descripción general de las funciones de clasificación.

Cómo funciona RANK() con OVER (PARTITION BY)

La cláusula PARTITION BY divide los datos en particiones o subconjuntos. Cuando se utiliza con RANK(), significa que los datos se clasifican dentro de la partición. Cuando llega a la segunda partición, la clasificación se restablece para empezar desde uno.

Veamos de nuevo la consulta del principio de este artículo para aclarar esto.

SELECT
  original_language,
  title,
  author,
  sales,
  RANK() OVER (PARTITION BY original_language ORDER BY sales DESC)
    AS sales_rank
FROM books;

En esta consulta, RANK() se utiliza con PARTITION BY. La partición, en este caso, es original_language. La clasificación se realiza por ventas en orden descendente, tal y como se especifica en la cláusula ORDER BY.

La forma en que hemos escrito PARTITION BY y ORDER BY significa que los libros se clasifican por ventas pero dentro de cada categoría lingüística. Una vez que la función clasifica todos los libros en un idioma, vuelve a empezar cuando llega al segundo idioma, y así sucesivamente.

Esto se ve en el resultado de la consulta.

original_languagetitleauthorsalessales_rank
EnglishHarry Potter and the Philosopher's StoneJ. K. Rowling1201
EnglishThe HobbitJ. R. R. Tolkien1002
EnglishLolitaVladimir Nabokov503
EnglishWatership DownRichard Adams503
EnglishNineteen Eighty-FourGeorge Orwell305
FrenchThe Little PrinceAntoine de Saint-Exupéry2001
FrenchThe PlagueAlbert Camus122
FrenchThe StrangerAlbert Camus103
ItalianThe Name of the RoseUmberto Eco501
ItalianThe Adventures of PinocchioCarlo Collodi352
ItalianThe Divine ComedyDante Alighieri123
RussianWar and PeaceLeo Tolstoy361
RussianAndromeda NebulaIvan Yefremov202
SpanishDon QuixoteMiguel de Cervantes5001
SpanishOne Hundred Years of SolitudeGabriel García Márquez502

Hemos marcado cada partición con un color diferente para ver fácilmente las distintas particiones. El libro más vendido en inglés es Harry Potter y la piedra filosofal, de J.K. Rowling. A continuación se sitúa El Hobbit. Lolita y Watership Down ocupan el tercer puesto, ya que RANK() asigna el mismo rango a libros con el mismo valor de ventas. A continuación, se salta un valor del ranking y Diecinueve Ochenta y Cuatro ocupa el quinto lugar.

La siguiente partición es la lengua francesa, y se reinicia la clasificación. El Principito es el libro más vendido en francés.

Vemos que la misma lógica funciona para los libros en italiano, ruso y español.

Ejemplo adicional

Hemos estudiado estas consultas para mostrar cómo funciona RANK() OVER (PARTITION BY). Ahora, ¡a practicar!

Lo que sigue no es muy diferente de la primera consulta. No deberías tener problemas para aplicar lo que has aprendido.

Hay una columna en la tabla books llamada classify_under. En ella se especifica la categoría en la que se va a colocar cada libro en la librería.

Vamos a ordenar los libros por ventas para cada categoría.

SELECT
  classify_under,
  title,
  author,
  sales,
  RANK() OVER (PARTITION BY classify_under ORDER BY sales DESC)
    AS sales_rank
FROM books;

La consulta selecciona la columna classify_under en lugar de original_language en la primera consulta. Las demás columnas seleccionadas son las mismas.

También hay una diferencia en RANK(). Puesto que estamos clasificando por classify_under, esta es la columna que debe estar en la cláusula PARTITION BY.

Una vez más, queremos clasificar los libros por ventas en orden descendente. Esta es la clasificación:

classify_undertitleauthorsalessales_rank
ClassicsDon QuixoteMiguel de Cervantes5001
ClassicsLolitaVladimir Nabokov502
ClassicsThe Name of the RoseUmberto Eco502
ClassicsOne Hundred Years of SolitudeGabriel García Márquez502
ClassicsWar and PeaceLeo Tolstoy365
ClassicsNineteen Eighty-FourGeorge Orwell306
ClassicsThe PlagueAlbert Camus127
ClassicsThe StrangerAlbert Camus108
FantasyHarry Potter and the Philosopher's StoneJ. K. Rowling1201
FantasyThe HobbitJ. R. R. Tolkien1002
FantasyWatership DownRichard Adams503
KidsThe Little PrinceAntoine de Saint-Exupéry2001
KidsThe Adventures of PinocchioCarlo Collodi352
PoetryThe Divine ComedyDante Alighieri121
Science fictionAndromeda NebulaIvan Yefremov201

Por pura coincidencia, de nuevo hay cinco particiones. En la categoría "Clásicos", Don Quijote es el libro más vendido. A continuación, hay tres libros clasificados en segundo lugar. La secuencia de clasificación se salta hasta llegar a Guerra y Paz en quinto lugar. El resto de los clásicos se clasifican secuencialmente, ya que no hay más empates.

En otras categorías, no hay empates. "Poesía" y "Ciencia ficción" sólo tienen un libro en cada categoría. Por tanto, sólo existe el primer rango.

Para ver más ejemplos con otras funciones de ventana, vaya a nuestro artículo que explica cómo utilizar PARTITION BY.

Añada RANK() a su vocabulario SQL

Hemos visto los usos más típicos de la función de ventana RANK(). Aunque requiere un OVER (ORDER BY), la cláusula PARTITION BY desbloquea sus posibilidades. Convierte RANK() en una sofisticada herramienta para clasificar datos en una o más particiones con facilidad en su trabajo diario.

Para obtener más información y practicar RANK() y otras funciones de ventana (de clasificación), utilice nuestro Funciones de ventana curso. Obtendrás una explicación aún más detallada de la clasificación y la oportunidad de escribir mucho código en nuestros ejercicios.

¡Feliz clasificación!