StackOverflow 社区您好:
情况
我正在构建一个更新查询,根据每个记录相互比较的方式设置表中所有记录的排名。表格示例:
id | budget | cost | rank | rank_score
1 | 500 | 20 | ? | ?
2 | 400 | 40 | ? | ?
3 | 300 | 40 | ? | ?
因此在此表中,cost
在确定排名方面权重最大,其次是 budget
。因此,记录 #2 将排名较高,而记录 #3 将排在第二位,记录 #1 将排在最后。可以看到,如果两条记录有相同的 cost
,然后budget
打破平局。
现在,为了轻松跟踪这样的“重量”,我正在创建 rank_score
列,它将包含 cost
的串联和budget
。因此rank_score
上表为:
id | budget | cost | rank | rank_score
1 | 500 | 20 | ? | 20500
2 | 400 | 40 | ? | 40400
3 | 300 | 40 | ? | 40300
这个rank_score
可以这样填写:
UPDATE table_name
SET rank_score = CONCAT(cost, budget);
问题
到目前为止一切都还好。但现在问题来了。我需要一个纯整数 rank
列用于排序等多种内容,但最重要的是向用户显示其记录的排名。当然,这个rank
列将等于rank_scores的降序。但我找不到计算这个rank
的方法单个更新查询中的列,而无需在 php 中执行子查询、循环等。
我尝试过的
所以,一开始我试图获取 rank_score
计算如下:
SELECT id,
CONCAT(cost, budget) AS rank_score
FROM table_name ;
然后在 php 中循环所有这些rank_scores,只是为了构建一个如下的查询:
UPDATE table_name
SET rank_score = CASE id WHEN 1 THEN 20500 END,
rank = CASE id WHEN 1 THEN 3 END
WHERE id IN (1) ;
...当然,这个示例更新查询并不完整,因为它还有更多 WHEN THEN END
表中每条记录的子句。不用说,这很丑陋,尤其是当您期望拥有成千上万条记录时。
所以,总而言之,我已经有了计算 rank_score
的方法。 ,但我也想计算 rank
(= 排名分数的降序)在同一查询中,或者至少不进行疯狂的 php 循环和 CASE WHEN THEN END
条款。
感谢您阅读并思考本文;)
澄清
澄清@SJuan76所说的话:
我无法通过 php 分配排名,因为有时会向用户显示一次固定数量的记录(例如,他的用户页面: SELECT * WHERE user_id = 333
,可以获取 1、3 或 8 条记录)并且他需要知道每条记录的排名。在这种情况下,通过 php 分配排名不起作用,因为此类排名将与获取的记录相关,而不是与表中的所有记录相关。
最佳答案
首先,我会更改 budget
, cost
和rank_score
转换为整数或其他数字数据类型,而不是
UPDATE table_name
SET rank_score = CONCAT(cost, budget) ;
然后你会使用:
UPDATE table_name
SET rank_score = cost * 1000 + budget * 1 ;
这样就更容易了,因为您不必处理字符串函数,也不必拥有类似的东西:
SELECT *
FROM table_name
WHERE (conditions...)
ORDER BY rank_score DESC
(括号:将一个参数 ( 1000
) 设置得比另一个参数 ( 1
) 更高,相当于具有 cost, budget
的顺序。尝试检查一下:
SELECT *
FROM table_name
ORDER BY cost DESC
, budget DESC
所以,你完全可以删除 rank_score
总而言之,除非您当然打算使用各种参数值进行实验。
正如其他人指出的那样,拥有一个不存储数据而是存储计算的字段不是最佳实践。这是非规范化。相反,您可以保持表规范化,并让数据库在每次需要时进行计算:
SELECT id, budget, cost,
cost*1000 + budget*1 AS rank_score_calculated
FROM table_name
ORDER BY rank_score_calculated DESC
rank_score_calculated
上面的例子中没有存储。这样,您就不必在每次更改预算或成本或在表中添加新行时更新计算字段。
只有一个缺点。如果表确实很大,并且您需要大量用户经常执行查询(和计算),并且表更新非常频繁,那么它可能会减慢您的数据库速度。在这种情况下,人们应该开始考虑添加这样一个字段。
另一种情况是需要绝对 rank
跨越所有表格行,就像您的需要一样。因为 MySQL 没有“窗口”函数,所以很难用纯 SQL 编写这样的查询。)
可以使用 MySQL 变量计算排名
SELECT *
, @rownum:=@rownum+1 AS rank_calculated
FROM table_name
, (SELECT @rownum:=0) AS st
ORDER BY rank_score DESC
如果您想将这些值放入 rank
,使用:
UPDATE table_name
JOIN
( SELECT id
, @rownum:=@rownum+1 AS rank_calculated
FROM table_name
, (SELECT @rownum:=0) AS st
ORDER BY rank_score DESC
) AS r
ON r.id = table_name.id
SET table_name.rank = r.rank_calculated ;
以上两个查询不是纯SQL。您可以选择移动到另一个支持窗口函数的数据库系统,例如 Postgres、SQL-Server 或 Oracle。
关于MySQL帮助: Optimize Update Query that Sets Rank According to Order of Another Column,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6553308/