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中没有像rownum
, row_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