Funkcja rangowania w MySQL

155

Muszę sprawdzić rangę klientów. Tutaj dodaję odpowiednie standardowe zapytanie SQL ANSI dla mojego wymagania. Pomóż mi przekonwertować go do MySQL.

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

Czy jest jakaś funkcja sprawdzająca pozycję w MySQL?

Aadi
źródło

Odpowiedzi:

266

Jedną z opcji jest użycie zmiennej rankingowej, takiej jak:

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

(SELECT @curRank := 0)Część pozwala zmiennej inicjalizacji bez potrzeby dodatkowego SETpolecenia.

Przypadek testowy:

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

Wynik:

+------------+------+--------+------+
| 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
źródło
52
+1 za przebiegłą inicjalizację inline, to piękna sztuczka.
Charles
28
Czy nie poprosił jednak o przegrodę? Rozumiem partycje, że zestaw wyników miałby oddzielne rankingi dla mężczyzn i kobiet.
Jesse Dhillon
2
@Jesse: Jeśli tak jest, ostatnio odpowiedziałem na podobne pytanie: stackoverflow.com/questions/3162389/multiple-ranks-in-one-table
Daniel Vassallo
6
A jeśli chcę przyznać Annie i Bobowi rangę 4?
Fahim Parkar
8
To nie implementuje przykładu z pytania, ponieważ pomija partition by genderczęść funkcji analitycznej (która „numeruje” wartość rangi według płci, a nie dla ogólnego wyniku)
a_horse_with_no_name
53

Oto ogólne rozwiązanie, które przypisuje wierszom gęstą rangę nad partycjami. Wykorzystuje zmienne użytkownika:

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;

Zauważ, że przypisania zmiennych są umieszczone wewnątrz CASEwyrażenia. To (w teorii) dba o kolejność ewaluacji. IS NOT NULLDodaje obsługiwać typ danych konwersji i krótkie spięcie problemów.

PS: Można go łatwo przekonwertować na numer wiersza na partycji, usuwając wszystkie warunki, które sprawdzają remis.

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

Demo na db <> fiddle

Salman A
źródło
2
To rozwiązanie lub rozwiązanie Mukesha powinno być właściwym rozwiązaniem. Chociaż technicznie wydaje mi się, że oba rozwiązania reprezentują gęsty ranking, a nie regularną pozycję. Oto dobre wyjaśnienie różnic: sqlservercurry.com/2009/04/… .
modulitos
Czy możesz nam również powiedzieć, jak powinien wyglądać kod .php? Próbowałem śledzić, ale powyższy kod nie działa. Jak wprowadzić do formatu .php?
twórca
To rozwiązanie nie jest zbyt ogólne; nie zadziała, jeśli kolumna_pozycji ma wartość 0. sqlfiddle.com/#!2/9c5dd/1
mike
1
@mike Dodaj sekcję ELSE do instrukcji CASE:ELSE @rank_count := @rank_count + 1
Prince Odame
1
@abhash ORDER BY gender, age DESC?
Salman A,
52

Chociaż odpowiedź, która uzyskała najwięcej głosów, jest klasyfikowana, nie dzieli się na partycje, możesz dołączyć do siebie, aby całość została również podzielona:

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

Przypadek użycia

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

Odpowiedź :

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
źródło
to wspaniała odpowiedź właśnie dlatego, że potrzebuję zrobić ranking partycji. Dziękuję Panu!
Kim Stacks
IMO ma taką samą złożoność jak podselekcja w odpowiedzi @Sam Kidman: O (n ^ 2). Ale nie wiem, czy można to zrobić lepiej w MySQL.
xmedeko
Zajrzyj na onlamp.com/pub/a/mysql/2007/03/29/ ..., aby zapoznać się ze świetnym samouczkiem na ten sam temat
ferics2
Dołącz samodzielnie, aby uzyskać rangę! To wspaniale. Wreszcie rozwiązanie bez zmiennych i bez funkcji okna MySQL 8 . :)
Timo
24

Ulepszenie wersji Daniela, aby obliczyć percentyl wraz z rangą. Również dwie osoby z takimi samymi ocenami otrzymają tę samą 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

Wyniki zapytania o przykładowe dane -

+----+-------+------+---------------+---------------+-----------------+
| 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
źródło
1
Mimo że nie jest to optymalne pod względem wydajności, jest niesamowite!
Gaspa79
18

Połączenie odpowiedzi Daniela i Salmana. Jednak ranga nie zostanie określona, ​​ponieważ istnieje sekwencja kontynuacji z remisami. Zamiast tego pomija rangę do następnej. Tak więc maksymalna zawsze osiąga liczbę wierszy.

    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;

Schemat i przypadek testowy:

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

Wynik:

+------------+------+--------+------+--------------------------+-----------------+
| 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
źródło
1
Jestem nowy w MySQL, ale czy to rozwiązanie jest w porządku? W dokumentach MySQL mówi, że „kolejność obliczania wyrażeń obejmujących zmienne użytkownika jest niezdefiniowana”. dev.mysql.com/doc/refman/5.7/en/user-variables.html
narduk
13

Począwszy od MySQL 8, możesz wreszcie korzystać z funkcji okna także w MySQL: https://dev.mysql.com/doc/refman/8.0/en/window-functions.html

Twoje zapytanie można zapisać dokładnie w ten sam sposób:

SELECT RANK() OVER (PARTITION BY Gender ORDER BY Age) AS `Partition by Gender`, 
  FirstName, 
  Age,
  Gender 
FROM Person
Lukas Eder
źródło
Nie jest źle, po prostu nie działa ze starszymi wersjami SQL. plus to była kopia i przeszłość jego pytania, więc wydaje się, że nie pasuje do odpowiedzi.
newdark-it
4
@ brand-it Dla użytkowników MySQL 8+ ta odpowiedź jest ważna, ponieważ informuje nas, że ranking jest już dostępny. Gdybym nie przewinął się tak daleko, założyłbym, że wcześniejsze odpowiedzi były jedynym rozwiązaniem.
Steve Smith
1
@SteveSmith Dobra uwaga, dobrze jest mieć tę odpowiedź dla tych, którzy używają nowszej wersji MYSQL.
newdark-it,
Tak, zniechęca mnie wiele odpowiedzi ze zmiennymi użytkownika i blokami logicznymi. Nowa wersja MySQL pozwala zrobić to DUŻO prosto dzięki funkcji RANK (), która oferuje wbudowane grupowanie według partycji.
James Bond
5

@Sam, twój punkt widzenia jest doskonały w koncepcji, ale myślę, że źle zrozumiałeś, co dokumentacja MySQL mówi na podanej stronie - lub źle zrozumiałem :-) - i chciałem to dodać, aby jeśli ktoś poczuł się nieswojo z @ Daniel odpowiada, że ​​będą bardziej uspokojeni lub przynajmniej kopią trochę głębiej.

Widzisz "@curRank := @curRank + 1 AS rank"wnętrzeSELECT nie jest „jedną instrukcją”, jest jedną „atomową” częścią instrukcji, więc powinno być bezpieczne.

Dokument, do którego się odwołujesz, pokazuje przykłady, w których ta sama zmienna zdefiniowana przez użytkownika w 2 (atomowych) częściach instrukcji, na przykład "SELECT @curRank, @curRank := @curRank + 1 AS rank" .

Można by argumentować, że @curRankjest ono użyte dwukrotnie w odpowiedzi @ Daniela: (1) the "@curRank := @curRank + 1 AS rank"i (2) the, "(SELECT @curRank := 0) r"ale ponieważ drugie użycie jest częściąFROM klauzuli, jestem prawie pewien, że zostanie ocenione jako pierwsze; zasadniczo czyniąc to drugim i poprzedzającym stwierdzeniem.

W rzeczywistości na tej samej stronie z dokumentacją MySQL, do której się odwołałeś, zobaczysz to samo rozwiązanie w komentarzach - może to być miejsce, skąd wziął je @Daniel; tak, wiem, że to komentarze, ale to komentarze na oficjalnej stronie z dokumentacją i to ma jakąś wagę.

David Husnian
źródło
Nic z tego nie jest uzasadnione w dokumentacji. To tylko (niejasne) spekulacje. Jak wszystkie odpowiedzi, zarówno używając, jak i pisząc tę ​​samą zmienną, która według instrukcji nie jest wyraźnie zdefiniowana, chociaż podręcznik zawiera wiele nieprzydatnych tekstów na temat tego, co może działać tak, jak się spodziewasz, bez mówienia, czego według niego oczekujesz lub jakiego zastosowania opis niegwarantowanego zachowania to. PS Od wersji 8.0 przypisywanie zmiennych poza SET jest przestarzałe.
philipxy
4

Najprostszym sposobem określenia rangi danej wartości jest policzenie liczby wartości przed nią. Załóżmy, że mamy następujące wartości:

10 20 30 30 30 40
  • Wszystkie 30wartości są uważane za trzecie
  • Wszystkie 40wartości są uważane za szóste (ranga) lub 4 (gęsta ranga)

Wróćmy teraz do pierwotnego pytania. Oto kilka przykładowych danych, które są posortowane zgodnie z opisem w PO (oczekiwane rangi są dodane po prawej stronie):

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

Aby obliczyć RANK() OVER (PARTITION BY Gender ORDER BY Age)dla Sarah , możesz użyć tego zapytania:

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

Aby obliczyć RANK() OVER (PARTITION BY Gender ORDER BY Age)dla wszystkich wierszy, możesz użyć tego zapytania:

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

A oto wynik (połączone wartości są dodawane po prawej stronie):

+------+------+------------+    +-----------+-----+--------+
| 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
źródło
3

Jeśli chcesz umieścić w rankingu tylko jedną osobę, możesz wykonać następujące czynności:

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

Ranking ten odpowiada funkcji wyroczni RANK (gdzie, jeśli masz ludzi w tym samym wieku, uzyskują tę samą rangę, a kolejność nie jest kolejna).

Jest to trochę szybsze niż użycie jednego z powyższych rozwiązań w podzapytaniu i wybranie z niego, aby uzyskać ranking jednej osoby.

Można to wykorzystać do uszeregowania wszystkich, ale jest wolniejsze niż powyższe rozwiązania.

SELECT
  Age AS age_var,
(
  SELECT COUNT(Age) + 1
  FROM Person
  WHERE (Age < age_var)
 ) AS rank
 FROM Person
Sam Kidman
źródło
Może stać się znacznie wolniejszy niż powyższe rozwiązania, gdy liczba wierszy w Persontabeli wzrośnie. To O (n ^ 2) vs O (n) wolniej.
xmedeko
2

Aby uniknąć „ jednak ” w odpowiedzi Erandaca w połączeniu z odpowiedziami Daniela i Salmana, można użyć jednego z następujących „obejść podziału”

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

Ranking partycji w 3. wariancie w tym fragmencie kodu zwróci ciągłe numery rankingowe. doprowadzi to do struktury danych podobnej do rank() over partition bywyniku. Jako przykład patrz poniżej. W szczególności, partitionSequence zawsze będzie rozpoczynać się od 1 dla każdego nowego rankingu partycji , używając tej metody:

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
...
Maks
źródło
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
źródło