我有3个相当简单的表:
用户
user_id
1
2
3
radio_songs
song_id song
1 SomeName
2 OtherName
radio_rates
user_id song_id rate (from 1 to 5)
1 1 5
2 1 4
1 2 2
2 2 2
我编写了相当复杂的查询目标MySQL,根据 lower bound of Wilson score confidence interval for a Bernoulli parameter 计算歌曲的当前“位置”(排名)。
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
参数和:
ROW_NUMBER()
的任何方法查询工作正常,我对此非常满意,但是当我们有多个具有相同分数的歌曲时,由于排序结果排名可能会因同一SQL查询的执行而异。我希望通过从与目标歌曲具有相同分数的所有歌曲中获得MIN()
等级来避免这种情况,但是查询变得如此复杂以至于我在没有临时表的情况下如何做到这一点 - 这甚至是可能的?
我很感谢帮助,以及上述查询的效果/优化方面的任何建议。
我知道考虑简单地在歌曲表中添加另一个分数列并通过触发器在每次插入/更新时计算它是值得的,但我想尽可能避免这种情况并按需计算排名。因此SQL查询本身对我来说最重要。
提前谢谢。
答案 0 :(得分:1)
这可能对您有用:
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
和
(SELECT @row := 0, @rank := null, @last_score := null) r
在这一行
IF(@last_score = x.ci_lower_bound, @rank, @rank := @row) AS rank
仅当分数与最后一行相比发生变化时,才将排名设置为行号。如果得分相同,则使用最后一行的排名。
警告:以这种方式使用会话变量,在升级到新版本时,您的代码将始终面临返回意外结果的风险。如果它工作,那是因为引擎的实现方式。无法保证表达式将按预期顺序执行。
作为一般规则,除了在SET语句中,你永远不应该 为用户变量赋值并读取其中的值 声明。例如,要增加变量,这没关系:
SET @a = @a + 1;
对于其他语句,例如SELECT,您可能会得到结果 期待,但这不能保证。在以下声明中,您 可能会认为MySQL会首先评估@a然后做一个 第二个任务:
SELECT @a, @a:=@a+1, ...;
但是,涉及用户的表达式的评估顺序 变量未定义。