MySQL 5.6 - DENSE_RANK 类似没有 Order By 的功能

Posted

技术标签:

【中文标题】MySQL 5.6 - DENSE_RANK 类似没有 Order By 的功能【英文标题】:MySQL 5.6 - DENSE_RANK like functionality without Order By 【发布时间】:2018-09-16 09:46:36 【问题描述】:

我有一张这样的桌子:

+------+-----------+
|caseID|groupVarian|
+------+-----------+
|1     |A,B,C,D,E  |
+------+-----------+
|2     |A,B,N,O,P  |
+------+-----------+
|3     |A,B,N,O,P  |
+------+-----------+
|4     |A,B,C,D,F  |
+------+-----------+
|5     |A,B,C,D,E  |
+------+-----------+

我想获得一个新列nameVarian,这样相同的groupVarian 值具有由nameVarian 表示的相同排名(例如:v1、v2 等)。但是,分配给特定groupVariannameVarian 值应该按照caseID 的顺序(按照它们在表格中出现的顺序)。

输出应该是这样的:

+------+-----------+----------+
|caseID|groupVarian|namevarian
+------+-----------+----------+
|1     |A,B,C,D,E  |v1        |
+------+-----------+----------+
|2     |A,B,N,O,P  |v2        |
+------+-----------+----------+
|3     |A,B,N,O,P  |v2        |
+------+-----------+----------+
|4     |A,B,C,D,F  |v3        |
+------+-----------+----------+
|5     |A,B,C,D,E  |v1        |
+------+-----------+----------+

【问题讨论】:

你的 mysql 版本是多少? @madhur baiya 5.6 @MadhurBhaiya 好的,我试试,谢谢 【参考方案1】:

对于 MySQL 版本 (OP's version is 5.6):

问题陈述看起来需要DENSE_RANK 功能而不是groupVarian;然而事实并非如此。 As explained by @Gordon Linoff:

您似乎希望按照它们在 数据。

假设您的表名是t(请为您的代码相应地更改表名和字段名)。这是一个approach utilizing session variables(对于旧版本的 MySQL),给出了所需的结果(DB Fiddle):

SET @row_number = 0;
SELECT t3.caseID, 
       t3.groupVarian, 
       CONCAT('v', t2.num) AS nameVarian
FROM
  (
   SELECT 
     (@row_number:=@row_number + 1) AS num, 
     t1.groupVarian 
   FROM 
     (
      SELECT DISTINCT groupVarian 
      FROM t 
      ORDER BY caseID ASC 
     ) AS t1 
  ) AS t2 
INNER JOIN t AS t3 
  ON t3.groupVarian = t2.groupVarian 
ORDER BY t3.caseID ASC 

另外:我之前模拟DENSE_RANK 功能的尝试效果很好。尽管也可以稍微调整以前的查询以实现DENSE_RANK 功能。但是,以下查询更有效,因为它创建了较小的派生表,并避免了groupVarian 上的JOIN

SET @row_number = 1;
SET @group_varian = '';

SELECT inner_nest.caseID, 
       inner_nest.groupVarian, 
       CONCAT('v', inner_nest.num) as nameVarian 
FROM (
        SELECT 
            caseID, 
            @row_number:=CASE
                           WHEN @group_varian = groupVarian THEN @row_number
                           ELSE @row_number + 1
                         END AS num, 
            @group_varian:=groupVarian as groupVarian 
        FROM
            t  
        ORDER BY groupVarian
     ) AS inner_nest 
ORDER BY inner_nest.caseID ASC 

【讨论】:

我尝试过,但如果 groupVarian 列中的值与其他行具有相同的值,则变体名称具有相同的变体名称。 @daud 你得到了什么输出? 如果 grupVarian 的值相同,则名称 varian 的值相同,例如请参阅我的帖子。感谢您的帮助 @daud 请在运行我的查询后发布您得到的输出?确保 SET 命令是 start 也运行 @daud 终于找到了解决方案。请检查更新的答案!应该 100% 工作 :) 不要忘记相应地更改表和字段名称。【参考方案2】:

你可以使用DENSE_RANK(MySQL 8.0):

SELECT *, CONCAT('v', DENSE_RANK() OVER(ORDER BY groupVarian)) AS namevarian
FROM tab
ORDER BY CaseID;

db<>fiddle demo

【讨论】:

太棒了,但我仍在我的服务器上使用 mysql 5.7。谢谢你的帮助。我很感激【参考方案3】:

基本上,您想枚举变体。如果你只想要一个数字,那么你可以使用最小 id:

select t.*, min_codeId as groupVariantId
from t join
     (select groupVariant, min(codeId) as min_codeId
      from t
      group by groupVariant
     ) g
     on t.groupVariant = g.groupVariant;

但这并不是你想要的。您似乎希望它们按它们在数据中出现的顺序进行枚举。为此,您需要变量。这有点棘手,但是:

select t.*, rn as groupVariantId
from t join
     (select g.*,
             (@rn := if(@gv = groupvariant, @gv,
                        if(@gv := groupvariant, @gv+1, @gv+1)
                       )
             ) as rn
      from (select groupVariant, min(codeId) as min_codeId
            from t
            group by groupVariant
            order by min(codeId)
           ) g cross join
           (select @gv := '', @rn := 0) params
     ) g
     on t.groupVariant = g.groupVariant;

使用变量很棘手。一个重要的考虑因素:MySQL 不保证SELECT 中表达式的求值顺序。这意味着不应在一个表达式中分配一个变量,然后在另一个表达式中使用它们——因为它们可能以错误的顺序计算(另一个答案有这个错误)。

此外,order by 需要发生在子查询中。 MySQL 不保证变量赋值发生在排序之前。

【讨论】:

以上是关于MySQL 5.6 - DENSE_RANK 类似没有 Order By 的功能的主要内容,如果未能解决你的问题,请参考以下文章

如何在 MySQL 5.6 中比较类似 JSON 的文本?

MySQL 从 DENSE_RANK() 结果更新相同的表字段

mysql 5.6 在线DDL

MySQL:与dense_rank()over()的group by和partition的差异输出?

MySQL - 排序函数 Rank() Over()Dense_rank() Over()Row_number() Over()

MySql中row_number()rank()dense_rank() 的区别