Проблем:
Групирате данните си с GROUP BY
и би искал да показва само първия ред от всяка група.
Пример:
Нашата база данни има таблица с име exam_results
с данни в следната таблица:
first_name | фамилно_име | година | резултат |
---|---|---|---|
Джон | Клайн | 2020 | 40 |
Едит | Черно | 2020 | 43 |
Отметка | Джонсън | 2019 | 32 |
Лора | Лято | 2020 | 35 |
Кейт | Смит | 2019 | 41 |
Якоб | Черно | 2019 | 44 |
Том | Бенет | 2020 | 38 |
Емили | Кели | 2020 | 43 |
За всяка година нека намерим ученика с най-добрия result
. Ако в група има двама ученици, изравнени за най-добрия, ние произволно ще изберем един от тях за показване.
Решение:
WITH added_row_number AS ( SELECT *, ROW_NUMBER() OVER(PARTITION BY year ORDER BY result DESC) AS row_number FROM exam_results ) SELECT * FROM added_row_number WHERE row_number = 1;
Резултатът е:
first_name | фамилно_име | година | резултат | номер_ред |
---|---|---|---|---|
Якоб | Черно | 2019 | 44 | 1 |
Емили | Кели | 2020 | 43 | 1 |
Дискусия:
Първо, трябва да напишете CTE, в който да присвоите номер на всеки ред във всяка група. За да направите това, можете да използвате ROW_NUMBER()
функция. В OVER()
, посочвате групите, на които трябва да бъдат разделени редовете (PARTITION BY
) и реда, в който числата трябва да бъдат присвоени на редовете (ORDER BY
).
Разгледайте резултата от вътрешната заявка:
SELECT *, ROW_NUMBER() OVER(PARTITION BY year ORDER BY result DESC) AS row_number FROM exam_results;
first_name | фамилно_име | година | резултат | номер_ред |
---|---|---|---|---|
Якоб | Черно | 2019 | 44 | 1 |
Кейт | Смит | 2019 | 41 | 2 |
Отметка | Джонсън | 2019 | 32 | 3 |
Емили | Кели | 2020 | 43 | 1 |
Едит | Черно | 2020 | 43 | 2 |
Джон | Клайн | 2020 | 40 | 3 |
Том | Бенет | 2020 | 38 | 4 |
Лора | Лято | 2020 | 35 | 5 |
Вие задавате номерата на редовете във всяка група (т.е. година). Всеки ред има номер на ред въз основа на стойността на result
колона. Редовете са сортирани в низходящ ред поради DESC
ключова дума след ORDER BY result
. Дори ако има няколко реда в рамките на група, които имат една и съща стойност на result
, редовете все още получават различни номера. Тук Едит Блек и Емили Кели имат същия result
но различни номера на редовете. За да промените това поведение и да зададете същия номер на ред за същия резултат в рамките на група, използвайте RANK()
или DENSE_RANK()
вместо ROW_NUMBER()
.
Във външната заявка избирате всички данни от CTE (added_row_number
) и използвайте WHERE
условие, за да посочите кой ред да се показва от всяка група. Тук искаме да покажем първия ред, така че условието е row_number = 1
.
Имайте предвид, че можете лесно да промените решението, за да получите например втория ред от всяка група.
WITH added_row_number AS ( SELECT *, ROW_NUMBER() OVER(PARTITION BY year ORDER BY result DESC) AS row_number FROM exam_results ) SELECT * FROM added_row_number WHERE row_number = 2;
Ето резултата:
first_name | фамилно_име | година | резултат | номер_ред |
---|---|---|---|---|
Кейт | Смит | 2019 | 41 | 2 |
Едит | Черно | 2020 | 43 | 2 |
От друга страна, ако искате да получите редовете с втората най-висока стойност на result
във всяка група трябва да използвате DENSE_RANK()
функция. Докато ROW_NUMBER()
функцията създава последователни числа за всеки ред в група, което води до различни стойности, присвоени на редовете със същия резултат, DENSE_RANK()
функция дава едно и също число на редовете със същия резултат.
WITH added_dense_rank AS ( SELECT *, DENSE_RANK() OVER(PARTITION BY year ORDER BY result DESC) AS rank FROM exam_results ) SELECT * FROM added_dense_rank WHERE rank = 2;
first_name | фамилно_име | година | резултат | ранг |
---|---|---|---|---|
Кейт | Смит | 2019 | 41 | 2 |
Джон | Клайн | 2020 | 40 | 2 |
Можете да видите, че Джон Клайн има втората най-висока стойност на result (40)
за 2020 г. Джон Клайн всъщност е третото лице в групата, но първите двама ученици имат същия result
и двете имат rank = 1
.