ROW_NUMBER () у MySQL


281

Чи є приємний спосіб у MySQL відтворити функцію SQL Server ROW_NUMBER()?

Наприклад:

SELECT 
    col1, col2, 
    ROW_NUMBER() OVER (PARTITION BY col1, col2 ORDER BY col3 DESC) AS intRow
FROM Table1

Тоді я міг би, наприклад, додати умову обмежити intRowдо 1, щоб отримати один рядок з найвищим col3для кожної (col1, col2)пари.


9
Позначено тегом, greatest-n-per-groupщоб направити вас на подібні запитання.
Білл Карвін

1
Видалений Sql-Serverтег, оскільки цей елемент був найвищим голосом у комбінованому пошуку тегів, але насправді не стосується SQL Server.
Мартін Сміт

для простої функції номера mysql рядків, ознайомтеся з datamakessense.com/mysql-rownum-row-number-function
AdrianBR

Для MySQL єдині ефективні рішення обговорюються в mysql.rjweb.org/doc.php/groupwise_max . Багато оприлюднених рішень потребують повного сканування таблиці (або ще гірше).
Рік Джеймс

2
Безсоромно саморекламне рішення для реальної аналітичної функції ROW_NUMBER, RANK, DESNSE_RANK в MySQL
Кеннет Сю

Відповіді:


102

Я хочу, щоб рядок з одним найвищим col3 для кожної пари (col1, col2).

Це груповий максимум , одне з найбільш часто задаваних питань SQL (оскільки здається, що це повинно бути легко, але насправді це не так).

Я часто плачу за нульове самостійне приєднання:

SELECT t0.col3
FROM table AS t0
LEFT JOIN table AS t1 ON t0.col1=t1.col1 AND t0.col2=t1.col2 AND t1.col3>t0.col3
WHERE t1.col1 IS NULL;

"Отримайте рядки в таблиці, для яких жоден інший рядок із збігом col1, col2 не має вищого кола3." (Ви помітите це і більшість інших максимально групових рішень поверне кілька рядків, якщо більше одного ряду має однакові col1, col2, col3. Якщо це проблема, вам може знадобитися деяка післяобробка.)


2
Але що робити, якщо для пари (col1, col2) є два максимальних значення? Ви б закінчилися з двох рядів.
Павло

@Paul: так! Щойно додав примітку про це у відповіді тик тому. Зазвичай ви можете легко викидати небажані зайві рядки в шар додатку після чогось випадково, але якщо у вас багато рядків з одним і тим же col3, це може бути проблематично.
bobince

1
bobince, рішення стало досить популярним тут на SO, але у мене є питання. Рішення в основному таке, як якщо б хтось спробував знайти найбільший ідентифікатор із наступним запитом: SELECT t1.id FROM test t1 LEFT JOIN test t2 ON t1.id>t2.id WHERE t2.id IS NULL;Не вимагає цьогоn*n/2 + n/2 порівняння IS NULL для пошуку одного рядка? Чи трапляються якісь оптимізації, яких я не бачу? Я спробував задати подібне запитання до Білла в іншій темі, але він, схоже, його проігнорував.
newtover

2
@Paul - Щоб вирішити випадок, коли існує декілька рядків, які відповідають максимуму на групу, і ви хочете взяти лише один, ви завжди можете додати первинний ключ у логіці пункту ON, щоб розірвати зв'язок ... SELECT t0.col3 З ТАБЛИЦІ ЯК t0 ВЛІТТЯ ПРИЄДНАЙТЕСЬ таблицю AS t1 ON t0.col1 = t1.col1 AND t0.col2 = t1.col2 AND (t1.col3, t1.pk)> (t0.col3, t0.pk) ДЕ t1.col1 НУЛЬНІ;
Джон Армстронг - Xgc

2
Це було б SELECT t0.col3 FROM table AS t0 WHERE NOT EXISTS (select 1 from table AS t1 ON t0.col1=t1.col1 AND t0.col2=t1.col2 AND t1.col3>t0.col3)
зручніше

204

У MySQL відсутня функція ранжирування. Найближчим ви можете скористатися змінною:

SELECT t.*, 
       @rownum := @rownum + 1 AS rank
  FROM YOUR_TABLE t, 
       (SELECT @rownum := 0) r

то як би це працювало в моєму випадку? Мені знадобиться дві змінні, по одній для кожного з col1 і col2? Col2 потрібно було б якось скинути, коли col1 змінився ..?

Так. Якби це Oracle, ви могли використовувати функцію LEAD, щоб досягти максимального значення при наступному значенні. На щастя, Quassnoi обкладинки логіку того, що потрібно реалізувати в MySQL .


1
Хм .... так, як би це працювало в моєму випадку? Мені знадобиться дві змінні, по одній для кожного з col1 і col2? Col2 потрібно було б якось скинути, коли col1 змінився ..?
Павло

Дякую ... як я вже говорив вище, ця відповідь однаково прийнята як бобінець, але я можу поставити галочку лише одна :-)
Павло

9
Присвоєння та зчитування з визначених користувачем змінних у тому самому операторі не є надійним. це задокументовано тут: dev.mysql.com/doc/refman/5.0/en/user-variables.html : "Як правило, ви ніколи не повинні присвоювати значення змінної користувача та читати значення в межах одного виразу. Ви можете отримати очікувані результати, але це не гарантується. Порядок оцінки виразів, що містять змінні користувача, не визначений і може змінюватись на основі елементів, що містяться в заданому виписці. "
Роланд Буман

1
@Roland: Я тестував лише невеликі набори даних, у мене не було жодних проблем. Дуже погано MySQL ще не вирішив функціональність - запит був з 2008 року
OMG Ponies

2
Здається, це невизначена поведінка, як зазначає Роланд. наприклад, це дає абсолютно невірні результати для таблиці, яку я спробував:SELECT @row_num:=@row_num+1 AS row_number, t.id FROM (SELECT * FROM table1 WHERE col = 264 ORDER BY id) t, (SELECT @row_num:=0) var;
jberryman

81

Я завжди закінчуюсь за цією схемою. Враховуючи цю таблицю:

+------+------+
|    i |    j |
+------+------+
|    1 |   11 |
|    1 |   12 |
|    1 |   13 |
|    2 |   21 |
|    2 |   22 |
|    2 |   23 |
|    3 |   31 |
|    3 |   32 |
|    3 |   33 |
|    4 |   14 |
+------+------+

Ви можете отримати такий результат:

+------+------+------------+
|    i |    j | row_number |
+------+------+------------+
|    1 |   11 |          1 |
|    1 |   12 |          2 |
|    1 |   13 |          3 |
|    2 |   21 |          1 |
|    2 |   22 |          2 |
|    2 |   23 |          3 |
|    3 |   31 |          1 |
|    3 |   32 |          2 |
|    3 |   33 |          3 |
|    4 |   14 |          1 |
+------+------+------------+

Запустивши цей запит, який не потребує жодної змінної:

SELECT a.i, a.j, count(*) as row_number FROM test a
JOIN test b ON a.i = b.i AND a.j >= b.j
GROUP BY a.i, a.j

Сподіваюся, що це допомагає!


1
якщо стовпці VARCHAR або CHAR, як ви можете впоратися з цією структурою?
Тушар

3
Ви дивовижні Мості, я шукаю саме це
luckykrrish

Щойно я дав цю відповідь, використовуючи свою логіку для рядкового номера. Дякую.
Уцав

@Tushar операторів <, >, <=, >=ручка CHAR і типи даних VARCHAR на алфавітному порядку; Я очікую, саме те, що ви шукаєте.
alex

1
@AlmazVildanov, ви повинні мати можливість використовувати цей запит просто як фільтр підзапиту. row_numbers <= 2 І велике спасибі за цю відповідь. Мості, це ідеально!
Закс

61
SELECT 
    @i:=@i+1 AS iterator, 
    t.*
FROM 
    tablename AS t,
    (SELECT @i:=0) AS foo

1
Перший: = здається, відсутній у відповіді @OMG Ponies. Дякуємо за публікацію цього Пітера Джонсона.
sholsinger

Я здогадуюсь (SELECT @i: = 0) AS foo має бути першою таблицею в операторі FROM, особливо якщо в інших таблицях використовується sub-
select

Навіщо вам навіть потрібне ".. як foo"?
Том Чівертон

@TomChiverton Якщо він відсутній, ви отримуєте: "Код помилки: 1248. Кожна похідна таблиця повинна мати свій псевдонім"
ExStackChanger

1
Присвоєння рангу тут зовсім не визначене, і це навіть не відповідає на питання
jberryman

27

Перегляньте цю статтю, в ній показано, як імітувати SQL ROW_NUMBER () за допомогою розділу в MySQL. Я зіткнувся з цим самим сценарієм у впровадженні WordPress. Мені потрібен ROW_NUMBER (), а його там не було.

http://www.explodybits.com/2011/11/mysql-row-number/

У прикладі статті є використання одного розділу за полями. Щоб розділити додаткові поля, ви можете зробити щось подібне:

  SELECT  @row_num := IF(@prev_value=concat_ws('',t.col1,t.col2),@row_num+1,1) AS RowNumber
         ,t.col1 
         ,t.col2
         ,t.Col3
         ,t.col4
         ,@prev_value := concat_ws('',t.col1,t.col2)
    FROM table1 t,
         (SELECT @row_num := 1) x,
         (SELECT @prev_value := '') y
   ORDER BY t.col1,t.col2,t.col3,t.col4 

Використання concat_ws обробляє null. Я перевірив це на 3 полях за допомогою int, date та varchar. Сподіваюсь, це допомагає. Перегляньте статтю, як вона розбиває цей запит і пояснює його.


1
Дивовижно. Це фактично робить розділення. Дуже зручно
Стюарт Ватт

1
Якщо порівнювати самостійне приєднання, це набагато ефективніше, але є проблема з логікою, порядок повинен відбуватися перед обчисленням row_num, concat також не потрібен. `` `SELECT @row_num: = IF (@ prev_col1 = t.col1 AND @ prev_col2 = t.col2), @ row_num + 1, 1) AS RowNumber, t.col1, t.col2, t.col3, t.col4 , @ prev_col1: = t.col1, @ prev_col2: = t.col2 ВІД (ВИБРАТИ * З таблиці1 ЗАМОВЛЕННЯ col1, col2, col3) t, (SELECT @row_num: = 1, @ prev_col1: = '', @ prev_col2: = '') var `` `
Кеннет Сю

Якщо вам потрібно поставити це в підзапит, тоді додайте limit 18446744073709551615до order byпункту примусу .
xmedeko

concat_wsз нового рядка ''небезпечно: concat_ws('',12,3) = concat_ws('',1,23). Краще використовувати якийсь сепаратор '_'або використовувати розчин @Kenneth Xu.
xmedeko


25

З MySQL 8.0.0 і вище ви могли б користуватися віконними функціями.

1.4 Що нового у MySQL 8.0 :

Віконні функції.

MySQL тепер підтримує віконні функції, які для кожного рядка із запиту виконують обчислення, використовуючи рядки, пов'язані з цим рядком. До них відносяться такі функції, як RANK (), LAG () та NTILE (). Крім того, декілька існуючих функцій сукупності зараз можуть використовуватися як функції вікон; наприклад, SUM () і AVG ().

ROW_NUMBER () перевиконання :

Повертає номер поточного рядка в його розділі. Числа рядків варіюються від 1 до кількості рядків розділів.

ORDER BY впливає на порядок нумерації рядків. Без ORDER BY нумерація рядків не визначена.

Демонстрація:

CREATE TABLE Table1(
  id INT AUTO_INCREMENT PRIMARY KEY, col1 INT,col2 INT, col3 TEXT);

INSERT INTO Table1(col1, col2, col3)
VALUES (1,1,'a'),(1,1,'b'),(1,1,'c'),
       (2,1,'x'),(2,1,'y'),(2,2,'z');

SELECT 
    col1, col2,col3,
    ROW_NUMBER() OVER (PARTITION BY col1, col2 ORDER BY col3 DESC) AS intRow
FROM Table1;

Демонстрація DBFiddle


1
зітхне ... нарешті!
Б / у_Бай_Вже

15

Я також хотів би проголосувати за рішення Мості Мостачо з незначною зміною коду запиту:

SELECT a.i, a.j, (
    SELECT count(*) from test b where a.j >= b.j AND a.i = b.i
) AS row_number FROM test a

Що дасть той самий результат:

+------+------+------------+
|    i |    j | row_number |
+------+------+------------+
|    1 |   11 |          1 |
|    1 |   12 |          2 |
|    1 |   13 |          3 |
|    2 |   21 |          1 |
|    2 |   22 |          2 |
|    2 |   23 |          3 |
|    3 |   31 |          1 |
|    3 |   32 |          2 |
|    3 |   33 |          3 |
|    4 |   14 |          1 |
+------+------+------------+

для столу:

+------+------+
|    i |    j |
+------+------+
|    1 |   11 |
|    1 |   12 |
|    1 |   13 |
|    2 |   21 |
|    2 |   22 |
|    2 |   23 |
|    3 |   31 |
|    3 |   32 |
|    3 |   33 |
|    4 |   14 |
+------+------+

З тією лише різницею, що запит не використовує JOIN і GROUP BY, покладаючись на вкладений вибір.


Це повинно бути краще? Вони, здається, можуть бути квадратичними, але я не впевнений, як переплутати висновок EXPLAIN
jberryman

Насправді, вкладені селектори, як відомо, не дуже добре оптимізовані в MySQL, тому цей привід призначений лише для демонстрації техніки запитів. Приклади, що базуються на змінних вище, краще працюють у більшості практичних випадків.
abcdn

1
Я не переконаний, що будь-яка відповідь на основі змінної фактично використовує визначене поведінку ...
jberryman

Вибачте, я не впевнений, що зрозумів, що ви мали на увазі під "визначеною поведінкою". Ви маєте на увазі, що це не працює для вас, або ви просто стурбовані тим, що це не зафіксовано документально?
abcdn

1
"Не визначена поведінка" означає, що вона не задокументована для роботи та / або задокументована, щоб не гарантувалася робота. Дивіться цитати та посилання на документацію в коментарях на цій сторінці. Це може повернути те, що хтось (беззаперечно) хоче / здогадується / гіпотезує / фантазує. Для певних версій реалізації визначено, що вирази запитів, що використовують збільшення CASE та використання змінних, програмістами в Percona показано, що вони працюють, переглядаючи код. Це може змінитися з будь-яким випуском.
філіпсі

12

Я б визначив функцію:

delimiter $$
DROP FUNCTION IF EXISTS `getFakeId`$$
CREATE FUNCTION `getFakeId`() RETURNS int(11)
    DETERMINISTIC
begin
return if(@fakeId, @fakeId:=@fakeId+1, @fakeId:=1);
end$$

тоді я міг би зробити:

select getFakeId() as id, t.* from table t, (select @fakeId:=0) as t2;

Тепер у вас немає підзапросу, якого ви не можете мати у видах.


Працює з одним обмеженням: якщо ви виконаєте запит кілька разів, ви отримаєте постійно зростаючі підроблені документи для одного і того ж набору результатів
Stephan Richter

ви можете надіслати набір @fakeId = 0; кожного разу, коли ви хочете запустити запит, але не оптимально, але працює
jmpeace

Дійсно, якщо ви видалите DETERMINISTIC. Тоді підроблений ідентифікатор невірний при використанні порядку від. Чому це?
Кріс

8

запит на рядок_кілька в mysql

set @row_number=0;
select (@row_number := @row_number +1) as num,id,name from sbs

Це можна використовувати для UPDATE-запитів? Я намагаюся, але я отримую помилку "усічені дані для стовпця ...".
Дієго

1
Якщо хтось зацікавлений у використанні його в UPDATE, його потрібно використовувати як підзапит, щоб працювати. ОНОВЛЕННЯ <таблиця> SET <field> = (ВИБІР \ @row_number: = \ @row_number +1) ЗАМОВИТИ <стовпець вашого замовлення>; Стовпчик порядку визначає впорядкування значень рядків.
Дієго

8

Там немає Funtion , як rownum, row_num()в MySQL , але навпаки, як показано нижче:

select 
      @s:=@s+1 serial_no, 
      tbl.* 
from my_table tbl, (select @s:=0) as s;

4

Я вирішив, що найкраще працює, використовуючи такий запит:

SELECT 
    col1, col2, 
    (
        SELECT COUNT(*) 
        FROM Table1
        WHERE col1 = t1.col1
        AND col2 = t1.col2
        AND col3 > t1.col3
    ) AS intRow
FROM Table1 t1

Стовпці ПАРТІЄТЬ ВІД просто порівняти з '=' і розділити І. Стовпці ЗАМОВЛЕННЯ BY порівнюватимуться з "<" або ">" і розділяються АБО.

Я вважав це дуже гнучким, навіть якщо це трохи дорого.


4

Не можна імітувати функціональність ряду. Ви можете отримати очікувані результати, але ви, швидше за все, розчаруєтесь на якомусь етапі. Ось що говорить документація на mysql:

Для інших тверджень, таких як SELECT, ви можете отримати очікувані результати, але це не гарантується. У наступному твердженні ви можете подумати, що MySQL спочатку оцінить @a, а потім виконає завдання друге: SELECT @a, @a: = @ a + 1, ...; Однак порядок оцінювання виразів, що містять змінні користувача, не визначений.

З повагою, Георгій.


Я не стежу за цим. Як "@i: = @i + 1 як позиція" не є прямою заміною на "ROW_NUMBER ()" (порядок за сумою (балом)) в якості позиції "?
Том Чівертон

1
@TomChiverton Оскільки його поведінка не визначена, як там написано в посібнику.
філіпсі


2

Я не бачу жодної простої відповіді, яка б охоплювала частину "УЧАСТЬ ПО", ось ось моя:

SELECT
    *
FROM (
    select
        CASE WHEN @partitionBy_1 = l THEN @row_number:=@row_number+1 ELSE @row_number:=1 END AS i
        , @partitionBy_1:=l AS p
        , t.*
    from (
        select @row_number:=0,@partitionBy_1:=null
    ) as x
    cross join (
        select 1 as n, 'a' as l
        union all
        select 1 as n, 'b' as l    
        union all
        select 2 as n, 'b' as l    
        union all
        select 2 as n, 'a' as l
        union all
        select 3 as n, 'a' as l    
        union all    
        select 3 as n, 'b' as l    
    ) as t
    ORDER BY l, n
) AS X
where i > 1
  • Пункт ORDER BY повинен відображати вашу ROW_NUMBER потребу. Таким чином, вже є чітке обмеження: ви не можете одночасно мати кілька ROW_NUMBER "емуляцій" цієї форми.
  • Порядок "обчисленої колонки" має значення . Якщо у вас є mysql обчислити ці стовпці в іншому порядку, він може не працювати.
  • У цьому простому прикладі я додаю лише одну, але у вас може бути кілька частин "РОЗПОДІЛ"

        CASE WHEN @partitionBy_1 = part1 AND @partitionBy_2 = part2 [...] THEN @row_number:=@row_number+1 ELSE @row_number:=1 END AS i
        , @partitionBy_1:=part1 AS P1
        , @partitionBy_2:=part2 AS P2
        [...] 
    FROM (
        SELECT @row_number:=0,@partitionBy_1:=null,@partitionBy_2:=null[...]
    ) as x

1

Трохи запізнюючись, але може допомогти тому, хто шукає відповіді ...

Приклад між рядками / row_number - рекурсивний запит, який може використовуватися в будь-якому SQL:

WITH data(row_num, some_val) AS 
(
 SELECT 1 row_num, 1 some_val FROM any_table --dual in Oracle
  UNION ALL
 SELECT row_num+1, some_val+row_num FROM data WHERE row_num < 20 -- any number
)
SELECT * FROM data
 WHERE row_num BETWEEN 5 AND 10
/

ROW_NUM    SOME_VAL
-------------------
5           11
6           16
7           22
8           29
9           37
10          46

2
Вибачте, але наскільки я знаю, MySQL не підтримує загальних виразів таблиці .
Альваро Гонсалес

це зараз ... @ ÁlvaroGonzález MySQL 8 підтримує лише функції CTE та вікон, тому цю відповідь не має сенсу використовувати у старих версіях MySQL ..
Raymond Nijland

1

Це дозволяє досягти тієї ж функціональності, яку забезпечує ROW_NUMBER () AND PARTITION BY, в MySQL

SELECT  @row_num := IF(@prev_value=GENDER,@row_num+1,1) AS RowNumber
       FirstName, 
       Age,
       Gender,
       @prev_value := GENDER
  FROM Person,
      (SELECT @row_num := 1) x,
      (SELECT @prev_value := '') y
  ORDER BY Gender, Age DESC

1

Також трохи пізно, але сьогодні у мене була така ж потреба, тому я здійснив пошук в Google і, нарешті, простий загальний підхід, знайдений тут, у статті Пінала Дейва http://blog.sqlauthority.com/2014/03/09/mysql-reset-row -кілька для кожної групи-розділ-за рядком-номером /

Я хотів зосередитись на початковому питанні Павла (і це теж була моя проблема), тому я підсумовую своє рішення як робочий приклад.

Оскільки ми хочемо розділити два колони, я створив змінну SET під час ітерації, щоб визначити, чи була запущена нова група.

SELECT col1, col2, col3 FROM (
  SELECT col1, col2, col3,
         @n := CASE WHEN @v = MAKE_SET(3, col1, col2)
                    THEN @n + 1 -- if we are in the same group
                    ELSE 1 -- next group starts so we reset the counter
                END AS row_number,
         @v := MAKE_SET(3, col1, col2) -- we store the current value for next iteration
    FROM Table1, (SELECT @n := 0, @v := NULL) r -- helper table for iteration with startup values
   ORDER BY col1, col2, col3 DESC -- because we want the row with maximum value
) x WHERE row_number = 1 -- and here we select exactly the wanted row from each group

3 означає при першому параметрі MAKE_SET, що я хочу обидва значення в SET (3 = 1 | 2). Звичайно, якщо у нас немає двох або більше стовпців, що створюють групи, ми можемо усунути операцію MAKE_SET. Конструкція точно така ж. Це працює для мене як потрібно. Велике спасибі Піналу Дейву за його чітку демонстрацію.


1
Зауважте, що ORDER BYв підзапиті можна ігнорувати (див. Mariadb.com/kb/en/mariadb/… ). Запропонованим рішенням цього є додавання LIMIT 18446744073709551615до підзапиту, який змушує сортувати. Однак це може спричинити проблеми з продуктивністю і не дійсно для справді
вигадливих

1

Це також може бути рішенням:

SET @row_number = 0;

SELECT 
    (@row_number:=@row_number + 1) AS num, firstName, lastName
FROM
    employees

Він не робить жодних розділів, і це не суттєво відрізняється від вище цитованої відповіді
Caius Jard

1

MySQL підтримує ROW_NUMBER () з версії 8.0+ .

Якщо ви використовуєте MySQL 8.0 або пізнішої версії, перевірте це функцією ROW_NUMBER (). В іншому випадку у вас є емуляція функції ROW_NUMBER ().

Row_number () - це функція ранжування, яка повертає послідовне число рядка, починаючи з 1 для першого рядка.

для старшої версії,

SELECT t.*, 
       @rowid := @rowid + 1 AS ROWID
  FROM TABLE t, 
       (SELECT @rowid := 0) dummy;

1

Важливо: Будь ласка, подумайте про оновлення до 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)


Виправдання для цього немає. Так само, як і інші відповіді, які призначаються & читати з тієї ж змінної.
philipxy

Чи можете ви надати детальніше phil?
Caius Jard

Дивіться інші мої коментарі на цій сторінці. Сайт "Googling": stackoverflow.com "philipxy" місл-файл (встановити АБО призначити АБО призначити АБО написати) прочитати ": відповідь мною та повідомлення про помилку, пов'язане в коментарі мною на це запитання, де прийнята відповідь цитує посібник ще негайно у твердженнях, що це добре робити щось, що суперечить цьому. Прочитайте посібник про змінні та повторне призначення.
філіпсі


Я розумію вашу стурбованість
Caius Jard

0

Це не найнадійніше рішення - але якщо ви просто шукаєте створити розділений ранг на полі з лише кількома різними значеннями, можливо, буде непросто використовувати певний випадок, коли логіка має стільки змінних, скільки вам потрібно.

Щось подібне працювало для мене в минулому:

SELECT t.*, 
   CASE WHEN <partition_field> = @rownum1 := @rownum1 + 1 
     WHEN <partition_field> = @rownum2 := @rownum2 + 1 
     ...
     END AS rank
FROM YOUR_TABLE t, 
   (SELECT @rownum1 := 0) r1, (SELECT @rownum2 := 0) r2
ORDER BY <rank_order_by_field>
;

Сподіваюся, що має сенс / допомагає!


-1

Ця робота ідеально підходить для створення RowNumber, коли у нас є більше одного стовпця. У цьому випадку дві колонки.

SELECT @row_num := IF(@prev_value= concat(`Fk_Business_Unit_Code`,`NetIQ_Job_Code`), @row_num+1, 1) AS RowNumber, 
    `Fk_Business_Unit_Code`,   
    `NetIQ_Job_Code`,  
    `Supervisor_Name`,  
    @prev_value := concat(`Fk_Business_Unit_Code`,`NetIQ_Job_Code`)  
FROM (SELECT DISTINCT `Fk_Business_Unit_Code`,`NetIQ_Job_Code`,`Supervisor_Name`         
      FROM Employee    
      ORDER BY `Fk_Business_Unit_Code`, `NetIQ_Job_Code`, `Supervisor_Name` DESC) z,  
(SELECT @row_num := 1) x,  
(SELECT @prev_value := '') y  
ORDER BY `Fk_Business_Unit_Code`, `NetIQ_Job_Code`,`Supervisor_Name` DESC

-3
set @i = 1;  
INSERT INTO ARG_VALUE_LOOKUP(ARG_VALUE_LOOKUP_ID,ARGUMENT_NAME,VALUE,DESCRIPTION,UPDATE_TIMESTAMP,UPDATE_USER,VER_NBR,OBJ_ID) 
select @i:= @i+1 as ARG_VALUE_LOOKUP_ID,ARGUMENT_NAME,VALUE,DESCRIPTION,CURRENT_TIMESTAMP,'admin',1,UUID() 
FROM TEMP_ARG_VALUE_LOOKUP 
order by ARGUMENT_NAME;

1
Спробуйте відформатувати будь-які відповіді та дайте додатковий контекст щодо того, що ви намагаєтесь зробити. Наразі це не більше, ніж погано відформатований текст.
Яннік Меус

2
Схоже, це не має жодного стосунку до початкового питання. Якщо у вас є власне питання, будь ласка, задайте його окремо.
Jeroen Mostert

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.