【问题标题】:FIND_IN_SET too slow with GROUP_CONCAT (Dense Rank in MySQL)GROUP_CONCAT 的 FIND_IN_SET 太慢(MySQL 中的密集排名)
【发布时间】:2017-11-24 07:26:23
【问题描述】:

我有一个根据列的值计算密集排名的查询:

SELECT id,
       score1, 
       FIND_IN_SET
       ( 
         score1, 
          ( 
            SELECT GROUP_CONCAT(score1  ORDER BY score1  DESC) FROM scores 
          ) 
       ) as rank 
FROM score_news;

查询结果如下所示:

+----+--------+------+
| id | score1 | rank |
+----+--------+------+
|  1 |     15 |    1 |
|  2 |     15 |    1 |
|  3 |     14 |    3 |
|  4 |     13 |    4 |
+----+--------+------+

当分数增加 N 倍时,查询需要 Nx 更长的时间。有什么办法可以优化这个吗?我的桌子大小大约为 106

注意:我已经尝试过一种使用 mysql 用户变量的技术,但是当我在大型集合上运行它时得到的结果不一致。经过调查,我在 MySQL 文档中发现了这一点:

用户变量的评估顺序未定义,可能会改变 基于给定查询中包含的元素。在选择 @a,@a := @a+1 ...,你可能会认为 MySQL 会先评估 @a 然后 然后进行第二次分配,但更改查询(例如,通过 添加 GROUP BY、HAVING 或 ORDER BY 子句)可能会更改 评估...一般规则是永远不要给用户赋值 在语句的一部分中使用变量并在某些情况下使用相同的变量 同一语句的其他部分。你可能会得到你想要的结果 期望,但这不能保证。

我对用户变量的尝试:

SELECT
      a.id,
      @prev := @curr as prev,
      @curr := a.score1 as curr,
      @rank := IF(@rank = 0, @rank + 1, IF(@prev > @curr, @rank+@ties, @rank)) AS rank,
      @ties := IF(@prev = @curr, @ties+1, 1) AS ties
    FROM
      scores a,
      (
        SELECT
          @curr := null,
          @prev := null,
          @rank := 0,
          @ties := 1,
          @total := count(*) 
        FROM scores 
        WHERE score1 is not null 
      ) b
    WHERE
      score1 is not null 
    ORDER BY
      score1 DESC
   ) 

【问题讨论】:

  • 我正在尝试计算密集排名,因此两个完全相同的分数将获得相同的排名。
  • 它确实给了我密集的行列。
  • 为了清楚起见,编辑了我的问题,请检查。
  • 你用的是哪个版本的mysql?你能把你的尝试也加入变量吗?
  • @trincot 我已经添加了我的变量尝试,请检查。

标签: mysql sql


【解决方案1】:

使用变量的解决方案可以工作,但您需要首先对结果集进行排序,并且只有然后才能使用变量赋值:

SELECT a.id,
       @rank := IF(@curr = a.score1, @rank, @rank + @ties) AS rank,
       @ties := IF(@curr = a.score1, @ties + 1, 1) AS ties,
       @curr := a.score1 AS curr
FROM   (SELECT * FROM scores WHERE score1 is NOT NULL ORDER BY score1 DESC) a,
       (SELECT @curr := null, @rank := 0, @ties := 1) b

注意:我将curr 列放在select 子句的最后以保存一个变量。

【讨论】:

  • 太棒了,这对我有用而且速度超快,不过我还得再检查几个测试用例。
【解决方案2】:

您也可以使用以下查询来获得您的密集排名,而无需使用用户定义的变量

SELECT 
  a.*,
  (SELECT 
    COUNT(DISTINCT score1) 
  FROM
    scores b 
  WHERE a.`score1` < b.score1) + 1 rank 
FROM
  scores a 
ORDER BY score1 DESC

Demo

Demo 使用您的数据集

score1 列上的索引可能会对您有所帮助

【讨论】:

  • 如果您注意到,得分为 14 的行的排名为 3,而不是 2。我正在考虑如何修改您的查询以获得这样的结果。
  • @bitsapien 为什么你需要 3 而不是 2 ?它的第二高不是第三对吗?
  • 这个排名是用来计算百分位数的,所以上面的排名方式没有用。
  • @bitsapien 类似sqlfiddle.com/#!9/00aa66/3 ?但这些不是正确的排名,你可以说它们是缺失的排名
  • 这对我有用,但速度非常慢,比我原来的查询慢。不过感谢您的帮助:)
猜你喜欢
  • 1970-01-01
  • 2014-09-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-02-10
  • 2015-07-22
  • 2019-07-31
相关资源
最近更新 更多