在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)
的单行。
我希望每个(col1,col2)对的行具有单个最高col3。
这是一个groupwise maximum,这是最常见的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。”(您会注意到这一点,如果多行具有相同的col1,则大多数其他groupwise-maximum解决方案将返回多行,col2 ,col3。如果这是一个问题,你可能需要一些后期处理。)
查询mysql中的row_number
row_num()
我发现最好的解决方案是使用这样的子查询:
select
@s:=@s+1 serial_no,
tbl.*
from my_table tbl, (select @s:=0) as s;
PARTITION BY列只是与'='进行比较并用AND分隔。 ORDER BY列将与“<”或“>”进行比较,并以OR分隔。
我发现这很灵活,即使它有点贵。
无法模仿rownumber功能。您可能会得到您期望的结果,但您很可能会在某个阶段感到失望。这是mysql文档所说的:
对于其他语句,例如SELECT,您可能会得到您期望的结果,但这不能保证。在下面的语句中,您可能会认为MySQL将首先评估@a然后再进行一次赋值:SELECT @ a,@ a:= @ a + 1,...;但是,涉及用户变量的表达式的评估顺序是未定义的。
此致,乔治。
MariaDB 10.2正在实现“窗口函数”,包括RANK(),ROW_NUMBER()和其他一些东西:
set @row_number=0;
select (@row_number := @row_number +1) as num,id,name from sbs
根据Percona Live本月的演讲,他们进行了相当好的优化。
语法与Question中的代码相同。
我没有看到任何关于“PARTITION BY”部分的简单答案,所以这是我的:
SELECT
col1, col2,
(
SELECT COUNT(*)
FROM Table1
WHERE col1 = t1.col1
AND col2 = t1.col2
AND col3 > t1.col3
) AS intRow
FROM Table1 t1
有点晚,但也可能帮助寻找答案的人...
在rows / row_number示例之间 - 可以在任何SQL中使用的递归查询:
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
这允许在MySQL中实现与ROW_NUMBER()和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
也有点晚了,但今天我有同样的需求,所以我在谷歌搜索,最后在Pinal Dave的文章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
中找到了一个简单的通用方法
我想专注于保罗的原始问题(这也是我的问题)所以我总结了我的解决方案作为一个工作的例子。
因为我们想要对两列进行分区,所以我会在迭代期间创建一个SET变量,以确定是否已启动新组。
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
3表示MAKE_SET的第一个参数,我想要SET中的两个值(3 = 1 | 2)。当然,如果我们没有两列或更多列构造组,我们可以消除MAKE_SET操作。结构完全一样。这对我来说很有用。非常感谢Pinal Dave的明确演示。
从版本8.0+开始,MySQL支持ROW_NUMBER()。
如果您使用MySQL 8.0或更高版本,请检查ROW_NUMBER()函数。否则,您已模拟ROW_NUMBER()函数。
row_number()是一个排名函数,它返回一行的序号,从第一行的1开始。
对于旧版本,
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
MySQL中没有排名功能。你可以得到的最接近的是使用一个变量:
SELECT t.*,
@rownum := @rownum + 1 AS rank
FROM YOUR_TABLE t,
(SELECT @rownum := 0) r
那么在我的案例中如何运作呢?我需要两个变量,col1和col2各有一个?当col1改变时,Col2需要以某种方式重置..?
是。如果是Oracle,则可以使用LEAD函数在下一个值处达到峰值。值得庆幸的是,Quassnoi涵盖了the logic for what you need to implement in MySQL。
重要提示:请考虑升级到MySQL 8+并使用定义和记录的ROW_NUMBER()函数,并抛弃绑定到功能有限的古代MySQL版本的旧hacks
现在这里是其中一个黑客:
这里使用查询变量的答案大多数/全都似乎忽略了文档所说的事实(释义):
不要依赖于从上到下按顺序评估的SELECT列表中的项目。不要在一个SELECT项中分配变量,而在另一个SELECT项中使用它们
因此,他们会冒出错误答案的风险,因为他们通常会这样做
SET @row_number = 0;
SELECT
(@row_number:=@row_number + 1) AS num, firstName, lastName
FROM
employees
如果从下到上评估这些,行号将停止工作(没有分区)
所以我们需要使用保证执行顺序的东西。输入CASE WHEN:
SELECT t.*,
@rowid := @rowid + 1 AS ROWID
FROM TABLE t,
(SELECT @rowid := 0) dummy;
作为概述ld,prevcol的赋值顺序很重要 - 在我们为当前行分配值之前,必须将prevcol与当前行的值进行比较(否则它将是当前行的col值,而不是前一行的col值) 。
这是如何组合在一起的:
我们遇到这样的情况:
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
脚注:
SELECT
t.*,
ROW_NUMBER() OVER(PARTITION BY pcol1, pcol2, ... pcolX ORDER BY ocol1, ocol2, ... ocolX) rn
FROM
t
周围的括号非常重要。如果没有它们,你将为@pcolX分配null并且事情停止工作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
(@pcolX := colX) = null
当我们有多个列时,这对我来说非常适合创建RowNumber。在这种情况下两列。
select @pcol1 := CAST(null as INT), @pcol2 := CAST(null as DATE)
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;
我总是最终遵循这种模式。鉴于此表:
+------+------+
| 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
希望有所帮助!
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个字段进行了测试。希望这可以帮助。查看文章,因为它打破了这个查询并解释它。
从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;
我还投票支持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,而是依赖于嵌套选择。
我会定义一个函数:
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;
现在您没有子视图,视图中没有子查询。
在MySQL中没有像qazxsw poi,qazxsw poi这样的函数,但是如下所示:
rownum