MySQL 中的 ROW_NUMBER()

Posted

技术标签:

【中文标题】MySQL 中的 ROW_NUMBER()【英文标题】:ROW_NUMBER() in MySQL 【发布时间】:2009-12-12 23:58:44 【问题描述】:

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 的单行。

【问题讨论】:

简单的mysql行号函数,查看datamakessense.com/mysql-rownum-row-number-function 对于 MySQL,mysql.rjweb.org/doc.php/groupwise_max 中讨论了唯一有效的解决方案。许多公开的解决方案需要全表扫描(或更糟)。 在 MySQL 中为真正的analytical function ROW_NUMBER, RANK, DESNSE_RANK 无耻地自我推销解决方案 dba.stackexchange.com/questions/13703/… MySql 8 现在有 ROW_NUMBER() 和 RANK(),请看下面的答案 【参考方案1】:

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。

【讨论】:

嗯....那么在我的情况下如何工作?我需要两个变量,col1 和 col2 各一个?当 col1 改变时,Col2 需要以某种方式重置..? 谢谢...正如我上面所说,这个答案同样被接受 bobince 的,但我只能打勾 :-) 在同一语句中对用户定义的变量进行赋值和读取是不可靠的。这在此处记录:dev.mysql.com/doc/refman/5.0/en/user-variables.html:“作为一般规则,您永远不应该为用户变量赋值并在同一语句中读取该值。您可能会得到您期望的结果,但这不能保证。顺序涉及用户变量的表达式的评估值未定义,可能会根据给定语句中包含的元素而改变。" @Roland:我只在小型数据集上进行了测试,没有遇到任何问题。太糟糕了 MySQL 还没有解决这个功能——这个请求从 2008 年就开始了 这似乎是 Roland 指出的未定义行为。例如对于我尝试过的表,这给出了完全错误的结果: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;【参考方案2】:

我想要每个 (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、col2、col3,您会注意到这一点以及大多数其他分组最大解决方案将返回多行。如果这是一个问题,您可能需要一些后处理。)

【讨论】:

但是如果 (col1, col2) 对的 col3 有两个最大值怎么办?你最终会得到两行。 @Paul:是的!刚刚在 tic 前的答案中添加了一个注释。之后,您通常可以轻松地在应用层中随机删除不需要的额外行,但如果您有 很多 行都具有相同的 col3,则可能会出现问题。 bobince,该解决方案在 SO 上变得相当流行,但我有一个问题。解决方案基本上与有人尝试使用以下查询查找最大 id 相同: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 比较才能找到单行吗?有没有我看不到的优化?我试图在另一个帖子中向比尔提出类似的问题,但他似乎忽略了它。 @Paul - 为了解决存在多行匹配每个组的最大值并且您希望只获取一个的情况,您始终可以在 ON 子句逻辑中添加主键以打破平局。 .. 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, t1.pk) > (t0.col3, t0.pk ) WHERE t1.col1 为 NULL ; 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) 更易读【参考方案3】:

我总是遵循这种模式。鉴于此表:

+------+------+
|    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

希望有帮助!

【讨论】:

如果列是 VARCHAR 或 CHAR,你怎么能用这种结构处理呢? 你太棒了莫斯蒂,我正在寻找这个 刚刚使用您的 row_number 逻辑给出了这个answer。谢谢。 @Tushar 运算符 <><=>= 按字母顺序处理 CHAR 和 VARCHAR 数据类型;我希望,这正是您正在寻找的。​​span> @AlmazVildanov 您应该可以将此查询简单地用作过滤掉row_numbers <= 2 的子查询,非常感谢Mosty 的回答,非常完美!【参考方案4】:
SELECT 
    @i:=@i+1 AS iterator, 
    t.*
FROM 
    tablename AS t,
    (SELECT @i:=0) AS foo

【讨论】:

@OMG Ponies 答案中似乎缺少第一个 := 。感谢您发布此彼得约翰逊。 我猜 (SELECT @i:=0) AS foo 应该是 FROM 语句中的第一个表,尤其是如果其他表使用子选择时 你为什么还需要'.. as foo'? @TomChiverton 如果丢失,您会得到:“错误代码:1248。每个派生表都必须有自己的别名” 这里的排名分配完全未定义,这甚至不能回答问题【参考方案5】:

MySQL 8.0.0 及以上,您可以本机使用窗口函数。

1.4 What Is New in MySQL 8.0:

窗口函数。

MySQL 现在支持窗口函数,对于查询中的每一行,使用与该行相关的行执行计算。其中包括 RANK()、LAG() 和 NTILE() 等函数。此外,一些现有的聚合函数现在可以用作窗口函数;例如,SUM() 和 AVG()。

ROW_NUMBER() over_clause :

返回其分区中当前行的编号。行数范围从 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 Demo

【讨论】:

叹息...终于! 这个需要投票,因为错过这个我浪费了很多时间【参考方案6】:

查看这篇文章,它展示了如何在 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 处理空值。我使用 int、date 和 varchar 对 3 个字段进行了测试。希望这可以帮助。查看这篇文章,因为它分解了这个查询并对其进行了解释。

【讨论】:

太棒了。这实际上进行了分区。很方便 相比self join,效率要高很多,但逻辑有问题,order必须在计算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 FROM (SELECT * FROM table1 ORDER BY col1, col2, col3) t, (SELECT @row_num := 1, @prev_col1 := '', @prev_col2 : = '') 变量``` 如果你需要把它放到子查询中,然后添加limit 18446744073709551615强制order by子句。 concat_ws 带有空字符串 '' 很危险:concat_ws('',12,3) = concat_ws('',1,23)。最好使用一些分隔符'_' 或使用@Kenneth Xu 解决方案。 op 的链接已失效; archive of link here【参考方案7】:

我也会投票支持 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,而是依赖嵌套选择。

【讨论】:

这应该更好吗?它们似乎都是二次的,但我不确定如何解释 EXPLAIN 输出 事实上,众所周知,嵌套选择在 MySQL 中没有得到很好的优化,所以这个 anwser 只是为了演示一种查询技术。我想,上面基于变量的示例在大多数实际情况下效果更好。 我不相信任何基于变量的答案实际上都在使用定义的行为...... 对不起,我不确定我明白你所说的“定义的行为”是什么意思。您的意思是它对您不起作用,还是您只是担心它没有记录在案? “未定义的行为”意味着它没有被记录为工作和/或被记录为不能保证工作。请参阅此页面上 cmets 中的文档引用和链接。它可能返回一个人(不健全地)想要/猜测/假设/幻想的东西。对于某些版本的实现,某些使用 CASE 递增和使用变量的查询表达式已被 Percona 的程序员通过查看代码证明可以工作。这可能会随着任何版本而改变。【参考方案8】:

我会定义一个函数:

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;

现在您没有子查询,而您不能在视图中拥有它。

【讨论】:

有一个限制:如果多次执行查询,相同结果集的 fakeId 会不断增加 你可以发送 set @fakeId =0;每次您想运行查询时,都不是最佳的,但可以工作 如果您删除 DETERMINISTIC,就会出现一个非常奇怪的问题。那么使用 order by 时 fakeId 不正确。这是为什么呢?【参考方案9】:

查询mysql中的row_number

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

【讨论】:

这可以用于 UPDATE 查询吗?我正在尝试,但出现“列的数据被截断...”错误。 如果有人有兴趣在 UPDATE 上使用它,它必须用作子查询才能工作。 UPDATE SET = (SELECT \@row_number := \@row_number +1) ORDER BY ; order 列决定了行的值排序。
【参考方案10】:

在 MySQL 中没有 rownumrow_num() 这样的功能,但方法如下:

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

【讨论】:

【参考方案11】:

重要提示:请考虑升级到 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 值,而不是前一行的列值)。

这是如何组合在一起的:

评估第一个 WHEN。如果这一行的 col 与前一行的 col 相同,则 @r 递增并从 CASE 返回。此返回 led 值存储在 @r 中。 MySQL 的一个特性是赋值将赋值给@r 的新值返回到结果行中。

对于结果集的第一行,@prevcol 为 null(在子查询中初始化为 null),因此该谓词为 false。每次 col 更改时,第一个谓词也会返回 false(当前行与前一行不同)。这会导致第二个 WHEN 被评估。

第二个 WHEN 谓词始终为假,它的存在纯粹是为了给 @prevcol 分配一个新值。因为这一行的 col 与前一行的 col 不同(我们知道这一点是因为如果它相同,则将使用第一个 WHEN),我们必须分配新值以保留它以供下次测试。因为进行了赋值,然后赋值的结果与 null 进行比较,任何与 null 相等的东西都是假的,所以这个谓词总是假的。但至少评估它完成了保留这一行的 col 值的工作,因此可以根据下一行的 col 值评估它

因为第二个 WHEN 为假,这意味着在我们按 (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

脚注:

pcol 中的 p 表示“分区”,ocol 中的 o 表示“顺序”——在一般形式中,我从变量名中删除了“prev”以减少视觉混乱

(@pcolX := colX) = null 周围的括号很重要。没有它们,您将 null 分配给 @pcolX 并且事情停止工作

结果集也必须按分区列排序,这是一种折衷方案,以便与前一列进行比较。因此,您不能根据一列对行号进行排序,但将结果集排序到另一列您可能可以使用子查询来解决此问题,但我相信文档还指出,除非使用 LIMIT,否则子查询排序可能会被忽略,这可能会影响性能

1234563赋值)并且没有执行,它也会停止。根据我的经验,这似乎不会发生,但如果可以合理发生,我很乐意接受 cmets 并提出解决方案

在创建@pcolX 变量的子查询中,将创建@pcolX 的空值转换为列的实际类型可能是明智之举,即:select @pcol1 := CAST(null as INT), @pcol2 := CAST(null as DATE)

【讨论】:

没有理由这样做。就像分配给和读取同一变量的其他答案一样。 你能提供更多细节吗? 在此页面上查看我的其他 cmets。谷歌搜索'site:***.com“philipxy”mysql变量(设置或分配或分配或写入)读取':An answer by me和a bug report在this question的评论中链接,其中接受的答案立即引用了手册在声称可以做一些与之相矛盾的事情。阅读手册重新变量和重新分配。 MySQL Server Blog @zhongxiao37 您需要阅读整个答案。我详细解释了为什么第二个 when 子句的结构可以保证它总是错误的。如果您不想阅读整个答案,请按 Ctrl-F 输入 The second WHEN predicate is always false 并阅读以这句话开头的要点【参考方案12】:

我发现效果最好的解决方案是使用这样的子查询:

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 分隔。

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

【讨论】:

【参考方案13】:

无法模仿行号功能。你可能会得到你期望的结果,但你很可能会在某个阶段感到失望。 这是mysql文档所说的:

对于其他语句,例如 SELECT,您可能会得到您期望的结果,但这不能保证。在下面的语句中,您可能认为 MySQL 会先评估 @a,然后再进行赋值: 选择 @a, @a:=@a+1, ...; 但是,涉及用户变量的表达式的求值顺序是未定义的。

问候, 乔治。

【讨论】:

我不关注。 “@i := @i + 1 as position”如何不能直接替代“ROW_NUMBER() over (order by sum(score) desc) as position”? @TomChiverton 因为它的行为没有定义,正如手册所说的那样。【参考方案14】:

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

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

根据本月在 Percona Live 上的一次演讲,它们的优化相当不错。

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

【讨论】:

【参考方案15】:

MySQL 从 8.0+ 版本开始支持 ROW_NUMBER()

如果您使用 MySQL 8.0 或更高版本,请查看 ROW_NUMBER() 函数。 否则,您将模拟 ROW_NUMBER() 函数。

row_number() 是一个排名函数,它返回一行的序号,第一行从 1 开始。

对于旧版本,

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

【讨论】:

【参考方案16】:

这允许在 MySQL 中实现 ROW_NUMBER() AND PARTITION BY 提供的相同功能

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

【讨论】:

【参考方案17】:

我没有看到任何涵盖“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

【讨论】:

【参考方案18】:

这也可能是一个解决方案:

SET @row_number = 0;

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

【讨论】:

虽然它不做任何分区,但它与更高引用的答案没有显着不同【参考方案19】:

我认为你可以在这里使用 DENSE_RANK() 函数。 示例:

select `score`, DENSE_RANK() OVER( ORDER BY score desc ) as `rank` from Scores;

https://www.mysqltutorial.org/mysql-window-functions/mysql-dense_rank-function/

【讨论】:

这对我有用,谢谢【参考方案20】:

有点晚了,但也可能对寻找答案的人有所帮助...

Between rows/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

【讨论】:

抱歉,据我所知 MySQL 不支持common table expressions。 现在可以了...@ÁlvaroGonzález MySQL 8 仅支持 CTE 和窗口函数,因此在旧 MySQL 版本中使用此答案并没有真正意义..【参考方案21】:

也有点晚了,但今天我也有同样的需求,所以我在 Google 上搜索,最后在 Pinal Dave 的文章http://blog.sqlauthority.com/2014/03/09/mysql-reset-row-number-for-each-group-partition-by-row-number/ 中找到了一个简单的通用方法@

我想专注于 Paul 最初的问题(这也是我的问题),因此我将我的解决方案总结为一个工作示例。

因为我们想要对两列进行分区,所以我会在迭代期间创建一个 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 操作。构造完全相同。这根据需要为我工作。非常感谢 Pinal Dave 的清晰演示。

【讨论】:

请注意,子查询中的ORDER BY 可以被忽略(参见mariadb.com/kb/en/mariadb/…)。建议的解决方案是将LIMIT 18446744073709551615 添加到子查询中,这会强制进行排序。然而,这可能会导致性能问题,并且对于真正的大表无效:)【参考方案22】:

如果您的查询包含GROUP BY 语句,则使用交叉连接和逗号的解决方案将不起作用。对于这种情况,您可以使用子选择:

SELECT (@row_number := @row_number + 1) AS rowNumber, res.*
FROM
(
  SELECT SUM(r.amount) 
  FROM Results r 
  WHERE username = 1 
  GROUP BY r.amount
) res
CROSS JOIN (SELECT @row_number := 0) AS dummy

【讨论】:

你救救我兄弟!!【参考方案23】:

这不是最可靠的解决方案 - 但如果您只是想在一个只有几个不同值的字段上创建一个分区排名,那么在某些情况下使用与您一样多的变量的逻辑可能并不笨拙要求。

这样的事情过去对我有用:

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>
;

希望这有意义/有帮助!

【讨论】:

【参考方案24】:

MySQL 从版本 8 开始,支持 ROW_NUMBER(),因此您可以像在 SQL Server 中一样使用以下查询

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

我还在 Maria DB 10.4.21 中对其进行了测试。它也可以在那里工作。

【讨论】:

【参考方案25】:

当我们有多个列时,这非常适合我创建 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

【讨论】:

以上是关于MySQL 中的 ROW_NUMBER()的主要内容,如果未能解决你的问题,请参考以下文章

模拟 row_number() MySQL

DB2 中的 ROW_NUMBER()

前 MariaDB 版本中的 Row_Number() 窗口函数

mysql 怎么才能做到rownumber序号

MySQL - ROW_NUMBER() OVER()函数用法详解(分组排序)

MySQL 中用于插入的 ROW_NUMBER() 等效项[重复]