MySQL帮助: Optimize Update Query that Sets Rank According to Order of Another Column

标签 mysql rank

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 , costrank_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/

相关文章:

sql-server - TSql 根据分区和行号返回列

php - 如何合并多个表数据并将json编码为一个数组?

r - MATLAB 等效于 R 函数 rank()?

mysql - 从一个特定日期开始获取组中的不同值mysql

mysql - 多列重复条目检查 MySQL

mysql - MySQL中的排名函数

sql-server - 如何获得 SQL 中特定行的排名?

php - 以md5格式将密码插入数据库?

php - 使用 REGEX 分块 MySQL 查询

mysql - 3 个表的 SQL 初学者查询逻辑