Fonction de classement dans MySQL

155

J'ai besoin de connaître le rang des clients. Ici, j'ajoute la requête SQL standard ANSI correspondante pour mes besoins. S'il vous plaît, aidez-moi à le convertir en MySQL.

SELECT RANK() OVER (PARTITION BY Gender ORDER BY Age) AS [Partition by Gender], 
  FirstName, 
  Age,
  Gender 
FROM Person

Existe-t-il une fonction pour connaître le rang dans MySQL?

Aadi
la source

Réponses:

266

Une option consiste à utiliser une variable de classement, telle que la suivante:

SELECT    first_name,
          age,
          gender,
          @curRank := @curRank + 1 AS rank
FROM      person p, (SELECT @curRank := 0) r
ORDER BY  age;

La (SELECT @curRank := 0)pièce permet l'initialisation de la variable sans nécessiter une SETcommande séparée .

Cas de test:

CREATE TABLE person (id int, first_name varchar(20), age int, gender char(1));

INSERT INTO person VALUES (1, 'Bob', 25, 'M');
INSERT INTO person VALUES (2, 'Jane', 20, 'F');
INSERT INTO person VALUES (3, 'Jack', 30, 'M');
INSERT INTO person VALUES (4, 'Bill', 32, 'M');
INSERT INTO person VALUES (5, 'Nick', 22, 'M');
INSERT INTO person VALUES (6, 'Kathy', 18, 'F');
INSERT INTO person VALUES (7, 'Steve', 36, 'M');
INSERT INTO person VALUES (8, 'Anne', 25, 'F');

Résultat:

+------------+------+--------+------+
| first_name | age  | gender | rank |
+------------+------+--------+------+
| Kathy      |   18 | F      |    1 |
| Jane       |   20 | F      |    2 |
| Nick       |   22 | M      |    3 |
| Bob        |   25 | M      |    4 |
| Anne       |   25 | F      |    5 |
| Jack       |   30 | M      |    6 |
| Bill       |   32 | M      |    7 |
| Steve      |   36 | M      |    8 |
+------------+------+--------+------+
8 rows in set (0.02 sec)
Daniel Vassallo
la source
52
+1 pour l'initialisation en ligne sournoise, c'est une belle astuce.
Charles
28
N'a-t-il pas demandé une partition? Ma compréhension des partitions est que l'ensemble de résultats aurait des classements séparés pour les hommes et les femmes.
Jesse Dhillon
2
@Jesse: Si tel est le cas, j'ai récemment répondu à une question similaire: stackoverflow.com/questions/3162389/multiple-ranks-in-one-table
Daniel Vassallo
6
Et si je veux donner le rang 4 à Anne et Bob tous les deux?
Fahim Parkar
8
Cela ne met pas en œuvre l'exemple de la question car il manque la partition by genderpartie de la fonction analytique (qui "numérote" la valeur de rang par sexe et non pour le résultat global)
a_horse_with_no_name
53

Voici une solution générique qui attribue un rang dense sur la partition aux lignes. Il utilise des variables utilisateur:

CREATE TABLE person (
    id INT NOT NULL PRIMARY KEY,
    firstname VARCHAR(10),
    gender VARCHAR(1),
    age INT
);

INSERT INTO person (id, firstname, gender, age) VALUES
(1,  'Adams',  'M', 33),
(2,  'Matt',   'M', 31),
(3,  'Grace',  'F', 25),
(4,  'Harry',  'M', 20),
(5,  'Scott',  'M', 30),
(6,  'Sarah',  'F', 30),
(7,  'Tony',   'M', 30),
(8,  'Lucy',   'F', 27),
(9,  'Zoe',    'F', 30),
(10, 'Megan',  'F', 26),
(11, 'Emily',  'F', 20),
(12, 'Peter',  'M', 20),
(13, 'John',   'M', 21),
(14, 'Kate',   'F', 35),
(15, 'James',  'M', 32),
(16, 'Cole',   'M', 25),
(17, 'Dennis', 'M', 27),
(18, 'Smith',  'M', 35),
(19, 'Zack',   'M', 35),
(20, 'Jill',   'F', 25);

SELECT person.*, @rank := CASE
    WHEN @partval = gender AND @rankval = age THEN @rank
    WHEN @partval = gender AND (@rankval := age) IS NOT NULL THEN @rank + 1
    WHEN (@partval := gender) IS NOT NULL AND (@rankval := age) IS NOT NULL THEN 1
END AS rnk
FROM person, (SELECT @rank := NULL, @partval := NULL, @rankval := NULL) AS x
ORDER BY gender, age;

Notez que les affectations de variables sont placées à l'intérieur du CASE expression. Ceci (en théorie) s'occupe de l'ordre de la question d'évaluation. Le IS NOT NULLest ajouté pour gérer les problèmes de conversion de type de données et de court-circuit.

PS: Il peut facilement être converti en numéro de ligne sur la partition en supprimant toutes les conditions qui vérifient l'égalité.

| id | firstname | gender | age | rank |
|----|-----------|--------|-----|------|
| 11 | Emily     | F      | 20  | 1    |
| 20 | Jill      | F      | 25  | 2    |
| 3  | Grace     | F      | 25  | 2    |
| 10 | Megan     | F      | 26  | 3    |
| 8  | Lucy      | F      | 27  | 4    |
| 6  | Sarah     | F      | 30  | 5    |
| 9  | Zoe       | F      | 30  | 5    |
| 14 | Kate      | F      | 35  | 6    |
| 4  | Harry     | M      | 20  | 1    |
| 12 | Peter     | M      | 20  | 1    |
| 13 | John      | M      | 21  | 2    |
| 16 | Cole      | M      | 25  | 3    |
| 17 | Dennis    | M      | 27  | 4    |
| 7  | Tony      | M      | 30  | 5    |
| 5  | Scott     | M      | 30  | 5    |
| 2  | Matt      | M      | 31  | 6    |
| 15 | James     | M      | 32  | 7    |
| 1  | Adams     | M      | 33  | 8    |
| 18 | Smith     | M      | 35  | 9    |
| 19 | Zack      | M      | 35  | 9    |

Démo sur db <> fiddle

Salman A
la source
2
Cette solution, ou la solution de Mukesh, devrait être la bonne solution. Bien que techniquement, je pense que les solutions de vous deux représentent un classement dense et non un classement régulier. Voici une bonne explication des différences: sqlservercurry.com/2009/04/… .
modulitos
Pouvez-vous également nous dire comment le code .php doit être exactement? J'ai essayé de suivre, mais le code ci-dessus ne fonctionne pas. Comment entrer au format .php?
créateur
Cette solution n'est pas très générique; cela ne fonctionnera pas si rank_column a une valeur de 0. sqlfiddle.com/#!2/9c5dd/1
mike
1
@mike Ajoutez une section ELSE à l'instruction CASE:ELSE @rank_count := @rank_count + 1
Prince Odame
1
@abhash ORDER BY gender, age DESC?
Salman A du
52

Bien que la réponse la plus votée soit classée, elle ne se partitionne pas, vous pouvez faire une auto-jointure pour que le tout soit également partitionné:

SELECT    a.first_name,
      a.age,
      a.gender,
        count(b.age)+1 as rank
FROM  person a left join person b on a.age>b.age and a.gender=b.gender 
group by  a.first_name,
      a.age,
      a.gender

Cas d'utilisation

CREATE TABLE person (id int, first_name varchar(20), age int, gender char(1));

INSERT INTO person VALUES (1, 'Bob', 25, 'M');
INSERT INTO person VALUES (2, 'Jane', 20, 'F');
INSERT INTO person VALUES (3, 'Jack', 30, 'M');
INSERT INTO person VALUES (4, 'Bill', 32, 'M');
INSERT INTO person VALUES (5, 'Nick', 22, 'M');
INSERT INTO person VALUES (6, 'Kathy', 18, 'F');
INSERT INTO person VALUES (7, 'Steve', 36, 'M');
INSERT INTO person VALUES (8, 'Anne', 25, 'F');

Réponse :

Bill    32  M   4
Bob     25  M   2
Jack    30  M   3
Nick    22  M   1
Steve   36  M   5
Anne    25  F   3
Jane    20  F   2
Kathy   18  F   1
Rahul Agarwal
la source
c'est une réponse merveilleuse précisément parce que j'ai besoin de faire un classement de partition. Merci Monsieur!
Kim Stacks
IMO, il a la même complexité que la sous-sélection dans la réponse de @Sam Kidman: O (n ^ 2). Mais je ne sais pas s'il est possible de faire mieux dans MySQL.
xmedeko
Consultez onlamp.com/pub/a/mysql/2007/03/29/… pour un excellent tutoriel dans le même sens
ferics2
Rejoignez-nous pour obtenir le rang! C'est génial. Enfin, une solution sans variables et sans fonctions de fenêtre MySQL 8 . :)
Timo
24

Un ajustement de la version de Daniel pour calculer le percentile avec le rang. Deux personnes avec les mêmes notes obtiendront également le même rang.

set @totalStudents = 0;
select count(*) into @totalStudents from marksheets;
SELECT id, score, @curRank := IF(@prevVal=score, @curRank, @studentNumber) AS rank, 
@percentile := IF(@prevVal=score, @percentile, (@totalStudents - @studentNumber + 1)/(@totalStudents)*100),
@studentNumber := @studentNumber + 1 as studentNumber, 
@prevVal:=score
FROM marksheets, (
SELECT @curRank :=0, @prevVal:=null, @studentNumber:=1, @percentile:=100
) r
ORDER BY score DESC

Résultats de la requête pour un exemple de données -

+----+-------+------+---------------+---------------+-----------------+
| id | score | rank | percentile    | studentNumber | @prevVal:=score |
+----+-------+------+---------------+---------------+-----------------+
| 10 |    98 |    1 | 100.000000000 |             2 |              98 |
|  5 |    95 |    2 |  90.000000000 |             3 |              95 |
|  6 |    91 |    3 |  80.000000000 |             4 |              91 |
|  2 |    91 |    3 |  80.000000000 |             5 |              91 |
|  8 |    90 |    5 |  60.000000000 |             6 |              90 |
|  1 |    90 |    5 |  60.000000000 |             7 |              90 |
|  9 |    84 |    7 |  40.000000000 |             8 |              84 |
|  3 |    83 |    8 |  30.000000000 |             9 |              83 |
|  4 |    72 |    9 |  20.000000000 |            10 |              72 |
|  7 |    60 |   10 |  10.000000000 |            11 |              60 |
+----+-------+------+---------------+---------------+-----------------+
Mukesh Soni
la source
1
Même si ce n'est pas vraiment optimal en termes de performances, c'est génial!
Gaspa79
18

Combinaison de la réponse de Daniel et de Salman. Cependant, le rang ne sera pas donné car la séquence continue avec des liens existe. Au lieu de cela, il saute le rang au suivant. Donc, le maximum atteint toujours le nombre de lignes.

    SELECT    first_name,
              age,
              gender,
              IF(age=@_last_age,@curRank:=@curRank,@curRank:=@_sequence) AS rank,
              @_sequence:=@_sequence+1,@_last_age:=age
    FROM      person p, (SELECT @curRank := 1, @_sequence:=1, @_last_age:=0) r
    ORDER BY  age;

Schéma et scénario de test:

CREATE TABLE person (id int, first_name varchar(20), age int, gender char(1));

INSERT INTO person VALUES (1, 'Bob', 25, 'M');
INSERT INTO person VALUES (2, 'Jane', 20, 'F');
INSERT INTO person VALUES (3, 'Jack', 30, 'M');
INSERT INTO person VALUES (4, 'Bill', 32, 'M');
INSERT INTO person VALUES (5, 'Nick', 22, 'M');
INSERT INTO person VALUES (6, 'Kathy', 18, 'F');
INSERT INTO person VALUES (7, 'Steve', 36, 'M');
INSERT INTO person VALUES (8, 'Anne', 25, 'F');
INSERT INTO person VALUES (9, 'Kamal', 25, 'M');
INSERT INTO person VALUES (10, 'Saman', 32, 'M');

Production:

+------------+------+--------+------+--------------------------+-----------------+
| first_name | age  | gender | rank | @_sequence:=@_sequence+1 | @_last_age:=age |
+------------+------+--------+------+--------------------------+-----------------+
| Kathy      |   18 | F      |    1 |                        2 |              18 |
| Jane       |   20 | F      |    2 |                        3 |              20 |
| Nick       |   22 | M      |    3 |                        4 |              22 |
| Kamal      |   25 | M      |    4 |                        5 |              25 |
| Anne       |   25 | F      |    4 |                        6 |              25 |
| Bob        |   25 | M      |    4 |                        7 |              25 |
| Jack       |   30 | M      |    7 |                        8 |              30 |
| Bill       |   32 | M      |    8 |                        9 |              32 |
| Saman      |   32 | M      |    8 |                       10 |              32 |
| Steve      |   36 | M      |   10 |                       11 |              36 |
+------------+------+--------+------+--------------------------+-----------------+
Erandac
la source
1
Je suis nouveau sur MySQL mais cette solution est-elle correcte? Dans la documentation MySQL, "l'ordre d'évaluation des expressions impliquant des variables utilisateur n'est pas défini". dev.mysql.com/doc/refman/5.7/en/user-variables.html
narduk
13

À partir de MySQL 8, vous pouvez enfin utiliser les fonctions de fenêtre également dans MySQL: https://dev.mysql.com/doc/refman/8.0/en/window-functions.html

Votre requête peut être écrite exactement de la même manière:

SELECT RANK() OVER (PARTITION BY Gender ORDER BY Age) AS `Partition by Gender`, 
  FirstName, 
  Age,
  Gender 
FROM Person
Lukas Eder
la source
Ce n'est pas faux ne fonctionne tout simplement pas avec les anciennes versions de SQL. De plus, c'était un peu une copie et un passé de sa question, donc on n'a pas l'impression que cela correspond à la réponse.
newdark-it
4
@ brand-it Pour ceux de MySQL 8+, cette réponse est importante car elle nous permet de savoir que Rank est désormais disponible. Si je n'avais pas descendu aussi loin, je suppose que les réponses précédentes étaient la seule solution.
Steve Smith le
1
@SteveSmith Bon point, c'est bien d'avoir cette réponse pour ceux qui utilisent la nouvelle version de MYSQL.
newdark-it
Oui, je suis découragé par beaucoup de réponses avec les variables utilisateur et les blocs logiques. Une nouvelle version de MySQL permet de le faire TRÈS simple avec la fonction RANK () qui offre un regroupement intégré par partitions.
James Bond
5

@Sam, votre point est excellent dans le concept, mais je pense que vous avez mal compris ce que disent les documents MySQL sur la page référencée - ou je ne comprends pas :-) - et je voulais juste ajouter ceci pour que si quelqu'un se sent mal à l'aise avec le @ Réponse de Daniel, ils seront plus rassurés ou du moins creuseront un peu plus.

Vous voyez que l' "@curRank := @curRank + 1 AS rank"intérieur SELECTn'est pas "une seule instruction", c'est une partie "atomique" de l'instruction, donc elle devrait être sûre.

Le document auquel vous faites référence continue pour montrer des exemples où la même variable définie par l'utilisateur dans 2 parties (atomiques) de l'instruction, par exemple, "SELECT @curRank, @curRank := @curRank + 1 AS rank" ,.

On pourrait dire que @curRankc'est utilisé deux fois dans la réponse de @ Daniel: (1) le "@curRank := @curRank + 1 AS rank"et (2) le "(SELECT @curRank := 0) r"mais puisque le deuxième usage fait partie de la FROMclause, je suis presque sûr qu'il est garanti d'être évalué en premier; ce qui en fait essentiellement une deuxième et précédente déclaration.

En fait, sur cette même page de documentation MySQL que vous avez référencée, vous verrez la même solution dans les commentaires - cela pourrait être d'où @Daniel l'a obtenu; oui, je sais que ce sont les commentaires, mais ce sont des commentaires sur la page officielle de la documentation et cela a un certain poids.

David Husnian
la source
Rien de tout cela n'est justifié par la documentation. Ce n'est que de la spéculation (floue). Comme toutes les réponses à la fois en utilisant et en écrivant la même variable, ce qui, selon le manuel, n'est pas explicitement défini, bien que le manuel contienne beaucoup de texte inutile sur ce qui pourrait fonctionner comme prévu, sans dire ce qu'il pense que vous attendez ou à quoi cela sert une description du comportement non garanti est. PS Depuis la version 8.0, l'attribution de variables en dehors de SET est obsolète.
philipxy
4

La solution la plus simple pour déterminer le rang d'une valeur donnée est de compter le nombre de valeurs devant elle. Supposons que nous ayons les valeurs suivantes:

10 20 30 30 30 40
  • Toutes les 30valeurs sont considérées comme 3ème
  • Toutes les 40valeurs sont considérées comme 6e (rang) ou 4e (rang dense)

Revenons maintenant à la question initiale. Voici quelques exemples de données triées comme décrit dans OP (les rangs attendus sont ajoutés à droite):

+------+-----------+------+--------+    +------+------------+
| id   | firstname | age  | gender |    | rank | dense_rank |
+------+-----------+------+--------+    +------+------------+
|   11 | Emily     |   20 | F      |    |    1 |          1 |
|    3 | Grace     |   25 | F      |    |    2 |          2 |
|   20 | Jill      |   25 | F      |    |    2 |          2 |
|   10 | Megan     |   26 | F      |    |    4 |          3 |
|    8 | Lucy      |   27 | F      |    |    5 |          4 |
|    6 | Sarah     |   30 | F      |    |    6 |          5 |
|    9 | Zoe       |   30 | F      |    |    6 |          5 |
|   14 | Kate      |   35 | F      |    |    8 |          6 |
|    4 | Harry     |   20 | M      |    |    1 |          1 |
|   12 | Peter     |   20 | M      |    |    1 |          1 |
|   13 | John      |   21 | M      |    |    3 |          2 |
|   16 | Cole      |   25 | M      |    |    4 |          3 |
|   17 | Dennis    |   27 | M      |    |    5 |          4 |
|    5 | Scott     |   30 | M      |    |    6 |          5 |
|    7 | Tony      |   30 | M      |    |    6 |          5 |
|    2 | Matt      |   31 | M      |    |    8 |          6 |
|   15 | James     |   32 | M      |    |    9 |          7 |
|    1 | Adams     |   33 | M      |    |   10 |          8 |
|   18 | Smith     |   35 | M      |    |   11 |          9 |
|   19 | Zack      |   35 | M      |    |   11 |          9 |
+------+-----------+------+--------+    +------+------------+

Pour calculer RANK() OVER (PARTITION BY Gender ORDER BY Age)pour Sarah , vous pouvez utiliser cette requête:

SELECT COUNT(id) + 1 AS rank, COUNT(DISTINCT age) + 1 AS dense_rank
FROM testdata
WHERE gender = (SELECT gender FROM testdata WHERE id = 6)
AND age < (SELECT age FROM testdata WHERE id = 6)

+------+------------+
| rank | dense_rank |
+------+------------+
|    6 |          5 |
+------+------------+

Pour calculer RANK() OVER (PARTITION BY Gender ORDER BY Age)pour toutes les lignes que vous pouvez utiliser cette requête:

SELECT testdata.id, COUNT(lesser.id) + 1 AS rank, COUNT(DISTINCT lesser.age) + 1 AS dense_rank
FROM testdata
LEFT JOIN testdata AS lesser ON lesser.age < testdata.age AND lesser.gender = testdata.gender
GROUP BY testdata.id

Et voici le résultat (les valeurs jointes sont ajoutées à droite):

+------+------+------------+    +-----------+-----+--------+
| id   | rank | dense_rank |    | firstname | age | gender |
+------+------+------------+    +-----------+-----+--------+
|   11 |    1 |          1 |    | Emily     |  20 | F      |
|    3 |    2 |          2 |    | Grace     |  25 | F      |
|   20 |    2 |          2 |    | Jill      |  25 | F      |
|   10 |    4 |          3 |    | Megan     |  26 | F      |
|    8 |    5 |          4 |    | Lucy      |  27 | F      |
|    6 |    6 |          5 |    | Sarah     |  30 | F      |
|    9 |    6 |          5 |    | Zoe       |  30 | F      |
|   14 |    8 |          6 |    | Kate      |  35 | F      |
|    4 |    1 |          1 |    | Harry     |  20 | M      |
|   12 |    1 |          1 |    | Peter     |  20 | M      |
|   13 |    3 |          2 |    | John      |  21 | M      |
|   16 |    4 |          3 |    | Cole      |  25 | M      |
|   17 |    5 |          4 |    | Dennis    |  27 | M      |
|    5 |    6 |          5 |    | Scott     |  30 | M      |
|    7 |    6 |          5 |    | Tony      |  30 | M      |
|    2 |    8 |          6 |    | Matt      |  31 | M      |
|   15 |    9 |          7 |    | James     |  32 | M      |
|    1 |   10 |          8 |    | Adams     |  33 | M      |
|   18 |   11 |          9 |    | Smith     |  35 | M      |
|   19 |   11 |          9 |    | Zack      |  35 | M      |
+------+------+------------+    +-----------+-----+--------+
Salman A
la source
3

Si vous souhaitez classer une seule personne, vous pouvez effectuer les opérations suivantes:

SELECT COUNT(Age) + 1
 FROM PERSON
WHERE(Age < age_to_rank)

Ce classement correspond à la fonction oracle RANK (où si vous avez des personnes du même âge, elles obtiennent le même rang, et le classement qui suit n'est pas consécutif).

C'est un peu plus rapide que d'utiliser l'une des solutions ci-dessus dans une sous-requête et de la sélectionner pour obtenir le classement d'une personne.

Cela peut être utilisé pour classer tout le monde, mais c'est plus lent que les solutions ci-dessus.

SELECT
  Age AS age_var,
(
  SELECT COUNT(Age) + 1
  FROM Person
  WHERE (Age < age_var)
 ) AS rank
 FROM Person
Sam Kidman
la source
Cela peut devenir beaucoup plus lent que les solutions ci-dessus lorsque le nombre de lignes dans le Persontableau augmente. C'est O (n ^ 2) vs O (n) plus lent.
xmedeko
2

Pour éviter le " cependant " dans la réponse d'Erandac en combinaison des réponses de Daniel et Salman, on peut utiliser l'une des "solutions de contournement de partition" suivantes

SELECT customerID, myDate

  -- partition ranking works only with CTE / from MySQL 8.0 on
  , RANK() OVER (PARTITION BY customerID ORDER BY dateFrom) AS rank, 

  -- Erandac's method in combination of Daniel's and Salman's
  -- count all items in sequence, maximum reaches row count.
  , IF(customerID=@_lastRank, @_curRank:=@_curRank, @_curRank:=@_sequence+1) AS sequenceRank
  , @_sequence:=@_sequence+1 as sequenceOverAll

  -- Dense partition ranking, works also with MySQL 5.7
  -- remember to set offset values in from clause
  , IF(customerID=@_lastRank, @_nxtRank:=@_nxtRank, @_nxtRank:=@_nxtRank+1 ) AS partitionRank
  , IF(customerID=@_lastRank, @_overPart:=@_overPart+1, @_overPart:=1 ) AS partitionSequence

  , @_lastRank:=customerID
FROM myCustomers, 
  (SELECT @_curRank:=0, @_sequence:=0, @_lastRank:=0, @_nxtRank:=0, @_overPart:=0 ) r
ORDER BY customerID, myDate

Le classement de la partition dans la troisième variante de cet extrait de code renverra des numéros de classement continus. cela conduira à une structure de données similaire au rank() over partition byrésultat. A titre d'exemple, voir ci-dessous. En particulier, partitionSequence commencera toujours par 1 pour chaque nouveau partitionRank , en utilisant cette méthode:

customerID    myDate   sequenceRank (Erandac)
                          |    sequenceOverAll
                          |     |   partitionRank
                          |     |     | partitionSequence
                          |     |     |    | lastRank
... lines ommitted for clarity
40    09.11.2016 11:19    1     44    1   44    40
40    09.12.2016 12:08    1     45    1   45    40
40    09.12.2016 12:08    1     46    1   46    40
40    09.12.2016 12:11    1     47    1   47    40
40    09.12.2016 12:12    1     48    1   48    40
40    13.10.2017 16:31    1     49    1   49    40
40    15.10.2017 11:00    1     50    1   50    40
76    01.07.2015 00:24    51    51    2    1    76
77    04.08.2014 13:35    52    52    3    1    77
79    15.04.2015 20:25    53    53    4    1    79
79    24.04.2018 11:44    53    54    4    2    79
79    08.10.2018 17:37    53    55    4    3    79
117   09.07.2014 18:21    56    56    5    1   117
119   26.06.2014 13:55    57    57    6    1   119
119   02.03.2015 10:23    57    58    6    2   119
119   12.10.2015 10:16    57    59    6    3   119
119   08.04.2016 09:32    57    60    6    4   119
119   05.10.2016 12:41    57    61    6    5   119
119   05.10.2016 12:42    57    62    6    6   119
...
Max
la source
0
select id,first_name,gender,age,
rank() over(partition by gender order by age) rank_g
from person

CREATE TABLE person (id int, first_name varchar(20), age int, gender char(1));

INSERT INTO person VALUES (1, 'Bob', 25, 'M');
INSERT INTO person VALUES (2, 'Jane', 20, 'F');
INSERT INTO person VALUES (3, 'Jack', 30, 'M');
INSERT INTO person VALUES (4, 'Bill', 32, 'M');
INSERT INTO person VALUES (5, 'Nick', 22, 'M');
INSERT INTO person VALUES (6, 'Kathy', 18, 'F');
INSERT INTO person VALUES (7, 'Steve', 36, 'M');
INSERT INTO person VALUES (8, 'Anne', 25, 'F');
INSERT INTO person VALUES (9,'AKSH',32,'M');
Aditya
la source