Важливо: Будь ласка, подумайте про оновлення до MySQL 8+ та використовуйте визначену та задокументовану функцію ROW_NUMBER () та викопайте старі хаки, прив’язані до обмеженої стародавньої версії MySQL.
Тепер ось один з таких хак:
Тут відповіді, що в основному використовують змінні запиту, - усі, мабуть, ігнорують той факт, що документація говорить (перефразовується):
Не покладайтеся на елементи зі списку SELECT, що оцінюються в порядку від верху до низу. Не призначайте змінні в одному елементі SELECT і не використовуйте їх в іншому
Таким чином, існує ризик, що вони виправдають неправильну відповідь, оскільки зазвичай це роблять
select
(row number variable that uses partition variable),
(assign partition variable)
Якщо вони коли-небудь оцінюються знизу вгору, номер рядка припинить роботу (немає розділів)
Тому нам потрібно використовувати щось із гарантованим порядком виконання. Введіть випадок, коли:
SELECT
t.*,
@r := CASE
WHEN col = @prevcol THEN @r + 1
WHEN (@prevcol := col) = null THEN null
ELSE 1 END AS rn
FROM
t,
(SELECT @r := 0, @prevcol := null) x
ORDER BY col
Як контур ld важливий порядок присвоєння prevcol - prevcol має порівнюватися зі значенням поточного рядка, перш ніж ми присвоїмо йому значення з поточного рядка (інакше це було б поточне значення col рядків, а не значення col попереднього рядка) .
Ось як це поєднується:
Перший КОЛИ оцінюється. Якщо стовпчик цього рядка такий же, як і попередній рядка попереднього рядка, то @r збільшується і повертається з CASE. Ці значення, що повертаються, зберігаються в @r. Особливістю MySQL є те, що присвоєння повертає нове значення того, що присвоєно @r, у рядки результатів.
Для першого ряду в наборі результатів @prevcol є null (він ініціалізований до null у підзапиті), тому цей предикат хибний. Цей перший присудок також повертає помилкові щоразу, коли змінюється col (поточний рядок відрізняється від попереднього рядка). Це викликає оцінку другої КОЛИ.
Другий присудок КОЛИ завжди хибний, і він суто існує, щоб присвоїти нове значення @prevcol. Оскільки стовпчик цього рядка відрізняється від кола попереднього рядка (ми знаємо це, тому що якби це було саме, перше КОЛО було б використано), ми повинні призначити нове значення, щоб зберегти його для тестування наступного разу. Оскільки присвоєння робиться, а потім результат присвоювання порівнюється з нульовим, а все, що прирівнюється до нуля, є помилковим, цей предикат завжди хибний. Але принаймні оцінюючи це зробило свою роботу, зберігаючи значення col з цього рядка, тож його можна оцінити по відношенню до значення col наступного рядка
Оскільки другий КОЛИ хибний, це означає, що в ситуаціях, коли стовпець, на який ми розділили (col), змінився, саме ELSE дає нове значення для @r, перезапустивши нумерацію з 1
Ми дістаємося до ситуації, коли це:
SELECT
t.*,
ROW_NUMBER() OVER(PARTITION BY pcol1, pcol2, ... pcolX ORDER BY ocol1, ocol2, ... ocolX) rn
FROM
t
Має загальну форму:
SELECT
t.*,
@r := CASE
WHEN col1 = @pcol1 AND col2 = @pcol2 AND ... AND colX = @pcolX THEN @r + 1
WHEN (@pcol1 := pcol1) = null OR (@pcol2 := col2) = null OR ... OR (@pcolX := colX) = null THEN null
ELSE 1
END AS rn
FROM
t,
(SELECT @r := 0, @pcol1 := null, @pcol2 := null, ..., @pcolX := null) x
ORDER BY pcol1, pcol2, ..., pcolX, ocol1, ocol2, ..., ocolX
Виноски:
P in pcol означає "розділ", o в ocol означає "порядок" - у загальній формі я випустив "prev" з назви змінної, щоб зменшити візуальне захаращення
Дужки навколо (@pcolX := colX) = null
важливі. Без них ви призначите null @pcolX, і все перестане працювати
Це компроміс, що набір результатів повинен бути впорядкований і стовпцями розділів, для попереднього стовпця порівняти з робочим. Таким чином, ви не можете упорядкувати свій рядовий номер відповідно до одного стовпця, але ваш набір результатів упорядкований по іншому. Ви можете вирішити це за допомогою підзапитів, але я вважаю, що документи також стверджують, що впорядкування підзапитів може ігноруватися, якщо не використовується LIMIT, і це може вплинути виконання
Я не заглиблювався в нього, крім тестування того, що метод працює, але якщо є ризик, що предикати у другому КОЛІ будуть оптимізовані (що-небудь у порівнянні з null є null / false, тому чому турбує виконання завдання) і не виконується , він також зупиняється. Здається, це не трапляється на моєму досвіді, але я з радістю прийму коментарі та запропоную рішення, якщо це могло б відбутися розумно
Може бути розумним привести нулі, які створюють @pcolX, до фактичних типів ваших стовпців, у підзапиті, який створює змінні @pcolX, а саме: select @pcol1 := CAST(null as INT), @pcol2 := CAST(null as DATE)
greatest-n-per-group
щоб направити вас на подібні запитання.