MySQL中的ROW_NUMBER()

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,以获得每个(col1, col2)对具有最高col3的单个行。

我想要每个(col1,col2)对具有单个最高col3的行。

这是一个群体最大化 ,最常见的SQL问题之一(因为它看起来应该很容易,但实际上并不是这样)。

我经常满足于自我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的行”(您会注意到,如果多行具有相同的col1,col2,则大多数其他groupwise-maximum解决scheme将返回多行,col3。如果这是一个问题,你可能需要一些后处理。)

MySQL中没有排名function。 你可以得到最接近的是使用一个variables:

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

那么在我的情况下如何工作呢? 我需要两个variables,每个col1和col2? 当col1改变时,Col2需要以某种方式重置。

是。 如果是Oracle,则可以使用LEAD函数在下一个值处达到峰值。 幸好,Quassnoi涵盖了你需要在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 | +------+------+------------+ 

通过运行这个查询,不需要定义任何variables:

 SELECT ai, aj, count(*) as row_number FROM test a JOIN test b ON ai = bi AND aj >= bj GROUP BY ai, aj 

希望有所帮助!

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

看看这篇文章,它展示了如何在MySQL中用分区模仿SQL ROW_NUMBER()。 我在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的。 我使用int,date和varchar对3个字段进行了testing。 希望这可以帮助。 查看文章,因为它打破了这个查询,并解释它。

我也会投票支持Mosty Mostacho的解决scheme,对他的查询代码进行小修改:

 SELECT ai, aj, ( SELECT count(*) from test b where aj >= bj AND ai = bi ) 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。

我会定义一个函数:

 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; 

现在你没有一个子查询,你不能在视图中。

在mysql中查询row_number

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

我发现最好的解决scheme是使用这样的子查询:

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

PARTITION BY列只是与'='进行比较,并用AND分隔。 ORDER BY列将与“<”或“>”进行比较,并由OR分隔。

我发现这是非常灵活的,即使它有点贵。

rownumberfunction不能被模仿。 你可能会得到你期望的结果,但是在某个阶段你很可能会感到失望。 这是什么MySQL文档说:

对于其他语句(如SELECT),您可能会得到您期望的结果,但这不能保证。 在下面的语句中,你可能会认为MySQL会首先评估@a,然后再做一个分配:SELECT @a,@a:= @ a + 1,…; 但是,涉及用户variables的expression式的评估顺序是不确定的。

问候,Georgi。

MariaDB 10.2正在实现“窗口函数”,包括RANK(),ROW_NUMBER()和其他一些东西:

https://mariadb.com/kb/en/mariadb/window-functions/

根据Percona Live在本月的一次演讲,他们已经相当优化。

语法与问题中的代码相同。

在MySQL中没有像rownumrow_num()那样的function,但是方法如下:

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

有点晚了,但也可以帮助寻找答案的人…

rows / row_number之间的示例 – 可以在任何SQL中使用的recursion查询:

 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中提供的function相同

 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 

也有点晚,但今天我有同样的需要,所以我search了谷歌,最后一个简单的一般方法在这里发现在Pinal Dave的文章http://blog.sqlauthority.com/2014/03/09/mysql-reset-row -number换各基团的分区按行数/

我想把重点放在保罗原来的问题上(这也是我的问题),所以我总结了我的解决scheme作为一个工作的例子。

因为我们想分割两列,我会在迭代过程中创build一个SETvariables来确定是否有一个新的组被启动。

 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)中。 当然,如果我们没有两个或更多的构build组的列,我们可以消除MAKE_SET操作。 施工完全一样。 这是根据需要为我工作。 非常感谢Pinal Dave的明确示范。

我没有看到有关“PARTITION BY”部分的简单答案,所以这里是我的:

 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以另一种顺序计算这些列,它可能不起作用。
  • 在这个简单的例子中,我只放了一个,但是可以有几个“PARTITION BY”部分

      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 

MySQL 8.0.0和更高版本,你可以本机使用窗口function。

1.4什么是MySQL 8.0中的新function :

窗口function。

MySQL现在支持窗口函数,对于查询中的每一行,使用与该行相关的行进行计算。 这些包括RANK(),LAG()和NTILE()等函数。 另外,现在可以使用几个现有的集合函数作为窗口函数。 例如SUM()和AVG()。

ROW_NUMBER()over_clause :

返回分区中当前行的编号。 行数范围从1到分区行数。

ORDER BY影响行编号的顺序。 没有ORDER BY,行编号是不确定的。

 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; 

当我们有多个列的时候,这对我来说非常适合创buildRowNumber。 在这种情况下是两列。

 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 
 SELECT col1, col2, count(*) as intRow FROM Table1 GROUP BY col1,col2 ORDER BY col3 desc