2

你好 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列,它将保存costand的连接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 分配排名不起作用,因为这样的排名将与获取的记录相关,而不是与表中的所有记录相关。

4

3 回答 3

2

首先,我会将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,除非您计划对各种参数值进行实验。

正如其他人所指出的,拥有一个不存储数据而是存储计算的字段并不是最佳实践。这是反规范化。Instaed,您保持表格规范化,并让数据库在您每次需要时进行计算:

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。

于 2011-07-01T22:13:06.633 回答
1

您是否尝试将其拆分为两个查询?还是使用子查询?

mysql> select p.*, (select count(0)+1 from table_name as s where s.cost >= p.cost and s.budget < p.budget) as rank from table_name as p where p.id in (1,2,3);
+----+------+--------+------+
| id | cost | budget | rank |
+----+------+--------+------+
|  1 |   20 |    500 |    3 |
|  2 |   40 |    400 |    2 |
|  3 |   40 |    300 |    1 |
+----+------+--------+------+
3 rows in set (0.00 sec)
于 2011-07-01T21:45:37.633 回答
0

SQL 已经可以根据需要对记录进行排序,而无需额外的表空间,并且(更重要的是)不会破坏正常形式。

并且排名可以直接从排序中获得,只需询问您想要的排序,当您检索数据时,在程序代码中从其顺序中添加索引。

为什么要在数据库中这样做?

于 2011-07-01T21:22:00.840 回答