Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/mysql/63.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Mysql 复杂SQL排序查询_Mysql_Sql_Performance_Query Optimization - Fatal编程技术网

Mysql 复杂SQL排序查询

Mysql 复杂SQL排序查询,mysql,sql,performance,query-optimization,Mysql,Sql,Performance,Query Optimization,我有3个相当简单的表格: 使用者 电台歌曲 song_id song 1 SomeName 2 OtherName 无线电频率 user_id song_id rate (from 1 to 5) 1 1 5 2 1 4 1 2 2 2 2 2 我编写了一个非常复杂的查询,目标是MySQL,它根据计算歌曲的当前“位置”(排名) SELECT ran

我有3个相当简单的表格:

使用者

电台歌曲

song_id  song
1        SomeName
2        OtherName
无线电频率

user_id  song_id  rate (from 1 to 5)
1        1        5
2        1        4
1        2        2
2        2        2
我编写了一个非常复杂的查询,目标是MySQL,它根据计算歌曲的当前“位置”(排名)

SELECT rank FROM(
    SELECT x.song AS song, x.ci_lower_bound AS ci_lower_bound, (@row:= @row + 1) AS rank FROM(
        SELECT song, ((SUM((rate - 1) * 0.25) + 1.9208) / (SUM((rate - 1) * 0.25) + SUM((5 - rate) * 0.25)) - 1.96 * SQRT((SUM((rate - 1) * 0.25) * SUM((5 - rate) * 0.25)) / (SUM((rate - 1) * 0.25) + SUM((5 - rate) * 0.25)) + 0.9604) / (SUM((rate - 1) * 0.25) + SUM((5 - rate) * 0.25))) / (1 + 3.8416 / (SUM((rate - 1) * 0.25) + SUM((5 - rate) * 0.25))) AS ci_lower_bound
        FROM radio_rates
        INNER JOIN radio_songs ON radio_rates.song_id = radio_songs.song_id 
        GROUP BY radio_rates.song_id
        ORDER BY ci_lower_bound DESC
    ) x, (SELECT @row := 0) r
) xx WHERE xx.song = @song
此查询基本上接受
@song
参数,并且:

  • 计算Wilson分数的下限,并按其降序排列
  • 将行号添加到每一行,因为我在MySQL中找不到任何方法来使用
    row\u number()
  • 最终得到了我们要找的歌曲的排名
该查询工作正常,我对此非常满意,但当我们有多首歌曲具有相同的分数时,由于排序,结果的排名可能会在执行相同的SQL查询时有所不同。我想通过从所有与目标歌曲得分相同的歌曲中获取
MIN()
rank来避免这种情况,但查询变得如此复杂,以至于我在没有临时表的情况下很难做到这一点——这可能吗

我非常感谢您的帮助,以及关于上述查询的性能/优化方面的任何建议

我知道,只需在歌曲表中添加另一个得分列,并通过触发器在每个插入/更新上计算它,都是值得的,但如果可能的话,我希望避免这一点,并按需计算排名。因此,SQL查询本身对我来说是最重要的


提前感谢您。

这可能适合您:

SELECT rank FROM(
    SELECT x.song AS song,
           (@row:= @row + 1) AS rn,
           IF(@last_score = x.ci_lower_bound, @rank, @rank := @row) AS rank
           (@last_score := x.ci_lower_bound) AS ci_lower_bound
    FROM(
        SELECT song, ((SUM((rate - 1) * 0.25) + 1.9208) / (SUM((rate - 1) * 0.25) + SUM((5 - rate) * 0.25)) - 1.96 * SQRT((SUM((rate - 1) * 0.25) * SUM((5 - rate) * 0.25)) / (SUM((rate - 1) * 0.25) + SUM((5 - rate) * 0.25)) + 0.9604) / (SUM((rate - 1) * 0.25) + SUM((5 - rate) * 0.25))) / (1 + 3.8416 / (SUM((rate - 1) * 0.25) + SUM((5 - rate) * 0.25))) AS ci_lower_bound
        FROM radio_rates
        INNER JOIN radio_songs ON radio_rates.song_id = radio_songs.song_id 
        GROUP BY radio_rates.song_id
        ORDER BY ci_lower_bound DESC
    ) x, (SELECT @row := 0, @rank := null, @last_score := null) r
) xx WHERE xx.song = @song
这些变化是:

SELECT x.song AS song,
       (@row:= @row + 1) AS rn,
       IF(@last_score = x.ci_lower_bound, @rank, @rank := @row) AS rank
       (@last_score := x.ci_lower_bound) AS ci_lower_bound

在这一行

IF(@last_score = x.ci_lower_bound, @rank, @rank := @row) AS rank
仅当分数与最后一行相比发生变化时,才将排名设置为行号。如果分数相同,则使用最后一行的排名

警告:以这种方式使用会话变量,在升级到新版本时,您的代码总是有返回意外结果的风险。如果它能工作,那是因为引擎是如何实现的。无法保证表达式将按预期顺序执行

一般来说,除了SET语句之外,您不应该 为用户变量赋值并读取同一变量中的值 陈述例如,要增加变量,可以这样做:

SET @a = @a + 1;
对于其他语句,例如SELECT,您可能会得到您想要的结果 期待,但这不是保证。在下面的声明中,您 可能认为MySQL会先对@a求值,然后再对@a求值 第二项任务:

SELECT @a, @a:=@a+1, ...;
但是,涉及用户的表达式的求值顺序 变量未定义


RANK()
在MySQL中实现是一件痛苦的事情
DENSE_RANK()
更容易一些。我知道,我讨厌自己从select中的用户定义变量开始,但在MySQL中没有其他方法可以获得行号功能-它在MariaDB 10.2中已经作为窗口函数提供,所以我希望很快就转到它,并在第一时间将其删除。你的解决方案当然有效,对此我非常感激。我只需将rank强制转换为int,因为我的服务器将其作为blob返回。非常感谢你的帮助!您可以使用带有自动插入列的临时表获得没有用户定义变量的行号。但要想获得你所需要的排名就有点复杂了。然后,您只需选择具有相同分数的
min(行号)
。@JustArchi尝试将
@rank:=null
更改为
@rank:=0
-然后您可能不需要强制转换
等级
SET @a = @a + 1;
SELECT @a, @a:=@a+1, ...;