Номер строки () в MySQL
есть ли хороший способ в 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
для каждого
21 ответ:
Я хочу строку с одним самым высоким col3 для каждой пары (col1, col2).
Это groupwise для максимальной один из наиболее часто задаваемых вопросов по SQL (поскольку кажется, что это должно быть легко, но на самом деле это не так).
Я часто пухлый для null-self-join:
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 имеет более высокий col3.(Вы заметите это и большинство других groupwise-максимальные решения вернут несколько строк, если несколько строк имеют одинаковый col1,col2,col3. Если это проблема, вам может понадобиться некоторая пост-обработка.)
в MySQL нет функции ранжирования. Самое близкое, что вы можете получить, это использовать переменную:
SELECT t.*, @rownum := @rownum + 1 AS rank FROM YOUR_TABLE t, (SELECT @rownum := 0) r
так как бы это работало в моем случае? Мне нужны две переменные, по одной для каждого из col1 и col2? Col2 нужно было бы сбросить как-то, когда col1 изменился..?
да. Если бы это был Oracle, вы могли бы использовать функцию LEAD для достижения пика при следующем значении. К счастью, квасной охватывает логика для того, что вам нужно реализовать в MySQL.
Я всегда в конечном итоге следую этой схеме. Учитывая эту таблицу:
+------+------+ | 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
надеюсь, что это поможет!
проверьте эту статью, она показывает, как имитировать 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 я проверил это против 3 полей, используя int, date и varchar. Надеюсь, это поможет. Ознакомьтесь со статьей, поскольку она разбивает этот запрос и объясняет его.
Я бы также проголосовал за решение Mosty Mostacho с незначительной модификацией его кода запроса:
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, полагаясь на вложенный select вместо этого.
С
MySQL 8.0.0
и выше вы можете изначально использовать оконные функции.окно функций.
MySQL теперь поддерживает оконные функции, которые для каждой строки из запроса выполняют вычисление с использованием строк, связанных с этой строкой. К ним относятся такие функции, как RANK (), LAG () и NTILE(). Кроме того, в качестве оконных функций теперь можно использовать несколько существующих агрегатных функций; например, SUM () и AVG ().
возвращает номер текущей строки в своем разделе. Номера строк варьируются от 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;
Я бы определил функции:
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;
теперь у вас нет подзапросов, которые вы не можете иметь в вид.
там нет funtion, как
rownum
,row_num()
в MySQL, но путь вокруг, как показано ниже:select @s:=@s+1 serial_no, tbl.* from my_table tbl, (select @s:=0) as s;
запрос row_number в mysql
set @row_number=0; select (@row_number := @row_number +1) as num,id,name from sbs
решение, которое я нашел, чтобы работать лучше всего было с помощью подзапроса, как это:
SELECT col1, col2, ( SELECT COUNT(*) FROM Table1 WHERE col1 = t1.col1 AND col2 = t1.col2 AND col3 > t1.col3 ) AS intRow FROM Table1 t1
раздел по столбцам просто сравнивается с ' = ' и разделяется на AND. Порядок по столбцам будет сравниваться с ' ' и разделяться OR.
Я нашел это очень гибким, даже если это немного дорого.
функциональность rownumber не может быть имитирована. Вы можете получить результаты, которые вы ожидаете, но вы, скорее всего, разочароваться на каком-то этапе. Вот что говорит документация mysql:
для других операторов, таких как SELECT, вы можете получить ожидаемые результаты, но это не гарантируется. В следующем заявлении вы можете подумать, что MySQL сначала оценит @a, а затем выполнит второе назначение: Выберите @a, @a:=@a+1, ...; Однако, порядок оценки выражения, включающие пользовательские переменные, не определены.
с уважением, Георгий.
MariaDB 10.2 реализует "оконные функции", включая RANK (), ROW_NUMBER() и несколько других вещей:
https://mariadb.com/kb/en/mariadb/window-functions/
основываясь на разговоре в Percona Live в этом месяце, они достаточно хорошо оптимизированы.
синтаксис идентичен коду в вопросе.
немного поздно, но также может помочь тому, кто ищет ответы...
между строками / 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
Это позволяет ту же функциональность, что ROW_NUMBER () и 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
также немного поздно, но сегодня у меня была такая же потребность, поэтому я искал в Google и, наконец, простой общий подход, найденный здесь в статье Pinal Davehttp://blog.sqlauthority.com/2014/03/09/mysql-reset-row-number-for-each-group-partition-by-row-number/
Я хотел сосредоточиться на первоначальном вопросе Павла (это была и моя проблема), поэтому я суммирую свое решение в качестве рабочего примера.
потому что мы хотим разбить на два столбца я бы создал Установите переменную во время итерации, чтобы определить, была ли запущена новая группа.
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 я хочу, чтобы оба значения в наборе (3=1/2). Конечно, если у нас нет двух или более столбцов, создающих группы, мы можем исключить операцию MAKE_SET. Конструкция точно такая же. Это работает для меня по мере необходимости. Большое спасибо Пиналу Дэйву за его четкую демонстрацию.
Я не вижу простого ответа, охватывающего часть" раздел по", Поэтому вот мой :
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
Это также может быть решением:
SET @row_number = 0; SELECT (@row_number:=@row_number + 1) AS num, firstName, lastName FROM employees
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;
эта работа идеально подходит для меня, чтобы создать 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