【问题标题】:Order results by time added, but rank them by vote count按添加时间排序结果,但按投票数排名
【发布时间】:2015-05-07 14:09:21
【问题描述】:

我正在编写一个网络应用程序,人们可以在其中添加想法并对想法进行投票。输出想法时,我希望它们按总票数或添加时间排序,但始终根据票数进行排名。

这就是我现在拥有的:

function get_ideas($status, $sortby, $count, $page){
    $offset = ($page - 1) * $count;
    $dbh = db_connect();
    if($sortby === 'popular'){
        $stmt = $dbh->prepare("
            SELECT i.idea_id, i.idea_datetime, i.user_id, i.idea_title, i.idea_text, 
            i.idea_vote_count, u.user_name, @curRank := @curRank + 1 AS rank 
            FROM ideas i 
            JOIN (SELECT @curRank := :rankoffset) AS q
            JOIN users u ON i.user_id = u.user_id
            WHERE idea_status = :idea_status 
            ORDER BY idea_vote_count DESC 
            LIMIT :count 
            OFFSET :offset;");
    } else {
        $stmt = $dbh->prepare("HOW DO I DO THIS???");
    }
    $stmt->bindParam(':idea_status', $status, PDO::PARAM_STR);
    $stmt->bindParam(':rankoffset', $offset, PDO::PARAM_INT);
    $stmt->bindParam(':count', $count, PDO::PARAM_INT);
    $stmt->bindParam(':offset', $offset, PDO::PARAM_INT);
    $stmt->execute();
    $result = $stmt->fetchAll(PDO::FETCH_ASSOC);
    $stmt = NULL;
    $dbh = NULL;
    return $result;
}

“if”块中的代码按预期工作 - 它返回一个按“idea_vote_count”排序且排名正确的数组。

但是,我不知道第二个语句应该是什么。只需将“ORDER BY”子句中的“idea_vote_count”更改为“idea_id”,就可以轻松地按添加的时间排序。但是,我如何获得排名?我想不出也找不到不涉及将排名存储在表格本身中的解决方案。

希望我已经清楚地解释了我的问题,但以防万一:

如何获得这样的表格:

idea_id | idea_vote_count
      1 |              20
      2 |              40
      3 |              30
      4 |               5

要产生这样的输出:

rank | idea_id | idea_vote_count
   4 |       4 |               5
   2 |       3 |              30
   1 |       2 |              40
   3 |       1 |              20

另外,我是 PHP 和 MySQL 的新手,所以如果您发现任何其他问题,请指出。

期待您的建议。谢谢:)

编辑:草莓:

我的想法表:

CREATE TABLE `ideas`(
`idea_id` int NOT NULL AUTO_INCREMENT,
`idea_datetime` datetime NOT NULL,
`user_id` int NOT NULL,
`idea_title` varchar(48) NOT NULL,
`idea_text` text NOT NULL,
`idea_vote_count` int NOT NULL DEFAULT 0,
`idea_status` varchar(16) NOT NULL DEFAULT 'active',
PRIMARY KEY(`idea_id`),
FOREIGN KEY(`user_id`) REFERENCES users(`user_id`)) 
ENGINE=INNODB;

示例创意由以下脚本生成。然后我手动将第 100 行的idea_vote_count 更改为第 5 行。

$i = 1;
set_time_limit(150);
while (i<101) {
    $datetime = date('Y-m-d h:m:s');
    $dbh->exec(INSERT INTO `ideas` (`idea_datetime`, `user_id`, `idea_title`, `idea_text`, `idea_vote_count`, `idea_status`) 
    VALUES ('{$datetime}', $i, 'Title{$i}', 'Text{$i}', $i, 'active');
    $i++
    sleep(1);
}

这是我在将 Strawberry 的 SQL 合并到我的函数后得到的结果:

function get_ideas($status, $sortby, $count, $page){
    $offset = ($page - 1) * $count;
    $dbh = db_connect();
    if($sortby === 'popular'){
        $stmt = $dbh->prepare("
        SELECT i.idea_id, i.idea_datetime, i.user_id, i.idea_title, i.idea_text, i.idea_vote_count, u.user_name, @curRank := @curRank + 1 AS rank 
        FROM ideas i 
        JOIN (SELECT @curRank := :rankoffset) AS q
        JOIN users u 
        ON i.user_id = u.user_id
        WHERE idea_status = :idea_status 
        ORDER BY idea_vote_count DESC 
        LIMIT :count 
        OFFSET :offset;");
    } else {
        $stmt = $dbh->prepare("
        SELECT n.*
        FROM (
            SELECT i.idea_id, i.idea_datetime, i.user_id, i.idea_title, i.idea_text, i.idea_vote_count, u.user_name, @curRank := @curRank + 1 AS rank 
            FROM ideas i 
            JOIN (SELECT @curRank := :rankoffset) AS q
            JOIN users u 
            ON i.user_id = u.user_id
            WHERE idea_status = :idea_status 
            ORDER BY idea_vote_count DESC 
            LIMIT :count 
            OFFSET :offset) n
        ORDER BY idea_id DESC;");
    }
    $stmt->bindParam(':idea_status', $status, PDO::PARAM_STR);
    $stmt->bindParam(':rankoffset', $offset, PDO::PARAM_INT);
    $stmt->bindParam(':count', $count, PDO::PARAM_INT);
    $stmt->bindParam(':offset', $offset, PDO::PARAM_INT);
    $stmt->execute();
    $result = $stmt->fetchAll(PDO::FETCH_ASSOC);
    $stmt = NULL;
    $dbh = NULL;
    return $result;
}

如您所见,该函数将 $count 和 $page 作为参数(通常它们的值为 $_REQUEST['count/page'])并根据它们计算偏移量和限制。这很重要,因为我不想同时向用户展示所有的想法,我想将它们分成几个页面。但是,这会以下列方式与选择/排名 SQL 混淆:

当 $page = 1 和 $count = 100 你得到 LIMIT 100 OFFSET 0 并且脚本按预期工作 - 它显示最近的行(第 100 行)作为排名第 96 的第一行(仅第 1、2、3 行, 4 的投票数较低),其次是其他最近的行,排名为 1、2、3 等等。

但是,当 $page = 1 和 $count = 10 时,您会得到 LIMIT 10 OFFSET 0 并且脚本首先输出第 99 行,因为它是评分最高的,但不是最新的。当 $page = 10 时,第 100 行成为结果集中的第一个结果(评级最低和最旧的行,尽管第 100 行是最新的)。

我可以在技术上选择整个表格,然后在 PHP 中处理分页,但我担心会影响性能。

EDIT2:我已将 OFFSET 和 LIMIT 移到外部 SELECT 中,现在一切正常。这样做的副作用是,内部 SELECT 选择整个表,但希望它不会变得太大而服务器无法处理。这是我暂时坚持的解决方案。它基于草莓的 SQL,所以我将其标记为答案。谢谢,草莓:)

【问题讨论】:

  • 您可以按照自己喜欢的方式对数组进行排序
  • 我知道,但我不知道如何按时间排序,同时计算与按票数排序一样的排名。
  • 对如此微小的数据集的性能影响可以忽略不计。如果你将数据集输出到 json,你甚至不需要 php.ini。 Javascript 可以处理分页和排序。

标签: php mysql ranking


【解决方案1】:

这是一个想法,使用纯 MySQL,但最好只返回带有排名的数据集,然后在应用程序代码中进行任何进一步的排序......

DROP TABLE IF EXISTS my_table;

CREATE TABLE my_table
(idea_id INT NOT NULL AUTO_INCREMENT PRIMARY KEY
,idea_vote_count INT NOT NULL
);

INSERT INTO my_table VALUES
(1 ,20),
(2 ,40),
(3 ,30),
(4 ,5);

SELECT n.* 
  FROM 
     ( SELECT x.*
            , @i:=@i+1 rank 
         FROM my_table x
            , (SELECT @i:=0) vars 
        ORDER 
           BY idea_vote_count DESC
     ) n 
 ORDER 
    BY idea_id DESC;
+---------+-----------------+------+
| idea_id | idea_vote_count | rank |
+---------+-----------------+------+
|       4 |               5 |    4 |
|       3 |              30 |    2 |
|       2 |              40 |    1 |
|       1 |              20 |    3 |
+---------+-----------------+------+

【讨论】:

  • 这在选择表中的所有行时有效,但在仅选择子集时无效。我的测试表实际上有大约 100 行。我给最后一行的投票计数为 5。当 $page = 1 和 $count = 100 时,它会正确返回所有内容(从 idea_id 100 开始,排名第 96)。但是,当 $page = 1 和 $count = 10 时,它从第 99 行开始。
  • 我不关注。这会以所需的方式返回样本数据集。它甚至提供适当的 DDL。
  • SELECT n.* FROM ( SELECT columns, @curRank := @curRank + 1 AS rank FROM idea i JOIN (SELECT @ curRank := :rankoffset) AS q JOIN users u ON i.user_id = u.user_id WHERE idea_status = :idea_status ORDER BY idea_vote_count DESC LIMIT :count OFFSET :offset) n ORDER BY idea_id DESC;" 这就是我最终得到的结果我将您的 SQL 调整到我的函数的其余部分(最显着的是分页)。我的实际测试表有 100 行,每行的投票计数都比前一个高 1,除了第 100 行设置为 5。(下面继续)
  • 通过设置 $page = 1 和 $count = 100(导致 LIMIT 100 OFFSET 0)从图片中取出分页时,您的 SQL 可以完美运行。这包括整个表并返回第 100 行(最近的行),排名第 96(只有第 1-4 行的投票数较少)作为第一个。然而,$page = 1 和 $count = 10 (LIMIT 10 OFFSET 0) 只选择 10 个最高评级的行 (99-90),尽管事实上第 100 行是最近的。当 $page = 10 时,第 100 行重新出现。
  • 不,你已经失去了我 - 但听起来你已经解决了你的问题。
猜你喜欢
  • 2012-10-29
  • 1970-01-01
  • 1970-01-01
  • 2020-11-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-08-16
  • 1970-01-01
相关资源
最近更新 更多