首页 文章

MySQL中的ROW_NUMBER()

提问于
浏览
237

有没有一种很好的方法在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 的单行 .

21 回答

  • -5
    SELECT 
        col1, col2, 
        count(*) as intRow
    FROM Table1
    GROUP BY col1,col2
    ORDER BY col3 desc
    
  • 92

    我希望每个(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 . 如果这是一个问题,你可能需要一些后期处理 . )

  • 11
    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;
    
  • 25

    查询mysql中的row_number

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

    查看这篇文章,它展示了如何在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个字段进行了测试 . 希望这可以帮助 . 查看文章,因为它打破了这个查询并解释它 .

  • 1

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

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

    希望有所帮助!

  • 187

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

    SET @row_number = 0;
    
    SELECT 
        (@row_number:=@row_number + 1) AS num, firstName, lastName
    FROM
        employees
    
  • 3

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

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

    此致,Georgi .

  • 1

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

    在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
    
  • 14

    This allows the same functionality that ROW_NUMBER() AND PARTITION BY provides to be achieved in 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
    
  • 55

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

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

  • 1

    我没有看到任何关于“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需求 . 因此,'s already a clear limitation: you can' t同时有几个这种形式的ROW_NUMBER "emulation" .

    • "computed column" matters 的顺序 . 如果你有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
    
  • 1

    也有点晚了,但今天我有同样的需求,所以我在谷歌搜索,最后在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的明确演示 .

  • 4

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

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

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

  • 6
    SELECT 
        @i:=@i+1 AS iterator, 
        t.*
    FROM 
        tablename AS t,
        (SELECT @i:=0) AS foo
    
  • -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 .

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

    现在你还没有子查询,您无法在视图中查看 .

  • 15

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

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

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

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

    根据Percona Live本月的演讲,他们进行了相当好的优化 .

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

  • 1

    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

相关问题