Mysql 复杂SQL排序查询
我有3个相当简单的表格: 使用者 电台歌曲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
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()
- 最终得到了我们要找的歌曲的排名
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, ...;