You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PHP在线考试系统使用order by rand()时出现题目重复问题求助

解决PHP在线考试系统中ORDER BY RAND()导致题目重复的问题

首先,咱们先拆解下你代码里的问题根源:

  • 你的第二个查询是SELECT * FROM question WHERE testid=? AND qnid=? ORDER BY RAND(),但qnid是题目的唯一标识啊!指定qnid后结果只会有一条数据,ORDER BY RAND()在这里完全没用,相当于白加。
  • 题目重复的核心原因是你没有正确生成不重复的随机题ID,或者$_SESSION['qn']没有随着答题流程更新,导致每次都加载同一道题。
  • 另外,mysql_*系列函数早就被PHP废弃了(PHP 7.0起直接移除),不仅有安全风险(比如SQL注入),还会导致兼容性问题,必须换掉。

下面给你两种靠谱的解决方案,任选其一即可:


方案一:预先生成随机题序(推荐)

考试开始时,一次性把当前试卷的所有题ID打乱顺序存入Session,之后按顺序取题,完全不会重复:

// 假设你已经用mysqli建立了数据库连接$conn
session_start();

// 进入考试页面时初始化题序(只执行一次)
if (!isset($_SESSION['question_order'])) {
    // 随机获取当前试卷的所有题ID
    $query = "SELECT qnid FROM question WHERE testid = ? ORDER BY RAND()";
    $stmt = $conn->prepare($query);
    $stmt->bind_param("i", $_SESSION['testid']);
    $stmt->execute();
    $result = $stmt->get_result();
    
    // 把题ID存入Session数组
    $_SESSION['question_order'] = [];
    while ($row = $result->fetch_assoc()) {
        $_SESSION['question_order'][] = $row['qnid'];
    }
    // 记录当前题的索引
    $_SESSION['current_qn_idx'] = 0;
}

// 加载当前题目
if (isset($_SESSION['stdname']) && isset($_SESSION['question_order'])) {
    $total_qns = count($_SESSION['question_order']);
    if ($_SESSION['current_qn_idx'] >= $total_qns) {
        echo "所有题目已完成!";
        exit;
    }
    
    // 获取当前题ID
    $_SESSION['qn'] = $_SESSION['question_order'][$_SESSION['current_qn_idx']];
    
    // 查询题目详情
    $query_qn = "SELECT * FROM question WHERE testid = ? AND qnid = ?";
    $stmt_qn = $conn->prepare($query_qn);
    $stmt_qn->bind_param("ii", $_SESSION['testid'], $_SESSION['qn']);
    $stmt_qn->execute();
    $question = $stmt_qn->get_result()->fetch_assoc();
    
    // 查询学生已有的回答
    $query_ans = "SELECT stdanswer, answered FROM studentquestion WHERE stdid = ? AND testid = ? AND qnid = ?";
    $stmt_ans = $conn->prepare($query_ans);
    $stmt_ans->bind_param("iii", $_SESSION['stdid'], $_SESSION['testid'], $_SESSION['qn']);
    $stmt_ans->execute();
    $student_answer = $stmt_ans->get_result()->fetch_assoc();
    
    // 点击「下一题」时执行这句,切换到下一道题
    // $_SESSION['current_qn_idx']++;
}

方案二:每次随机选未答的题目

如果不想预加载所有题,每次查询时直接从学生还没答过的题里随机选一道:

// 假设你已经用mysqli建立了数据库连接$conn
session_start();

if (isset($_SESSION['stdname'])) {
    // 随机获取一道未答的题ID
    $query = "SELECT q.qnid 
              FROM question q
              LEFT JOIN studentquestion sq 
                  ON q.qnid = sq.qnid 
                  AND sq.stdid = ? 
                  AND sq.testid = ?
              WHERE q.testid = ? 
                AND sq.qnid IS NULL  // 筛选未答的题
              ORDER BY RAND()
              LIMIT 1";
    
    $stmt = $conn->prepare($query);
    $stmt->bind_param("iii", $_SESSION['stdid'], $_SESSION['testid'], $_SESSION['testid']);
    $stmt->execute();
    $result = $stmt->get_result();
    $row = $result->fetch_assoc();
    
    if ($row) {
        $_SESSION['qn'] = $row['qnid'];
        
        // 查询题目详情
        $query_qn = "SELECT * FROM question WHERE testid = ? AND qnid = ?";
        $stmt_qn = $conn->prepare($query_qn);
        $stmt_qn->bind_param("ii", $_SESSION['testid'], $_SESSION['qn']);
        $stmt_qn->execute();
        $question = $stmt_qn->get_result()->fetch_assoc();
        
        // 查询学生已有的回答(如果中途返回可能存在)
        $query_ans = "SELECT stdanswer, answered FROM studentquestion WHERE stdid = ? AND testid = ? AND qnid = ?";
        $stmt_ans = $conn->prepare($query_ans);
        $stmt_ans->bind_param("iii", $_SESSION['stdid'], $_SESSION['testid'], $_SESSION['qn']);
        $stmt_ans->execute();
        $student_answer = $stmt_ans->get_result()->fetch_assoc();
    } else {
        echo "所有题目已完成!";
    }
}

关键注意事项

  1. 必须替换mysql_*函数:上面的示例用了mysqli预处理语句,能有效防止SQL注入,同时兼容最新PHP版本。
  2. Session的维护:要确保Session正常开启(代码开头加session_start()),并且考试结束后可以清空相关Session变量。
  3. 避免重复提交:如果是AJAX加载题目,要做好防重复提交的处理,防止同一题被多次选中。

内容的提问来源于stack exchange,提问作者Javed Ansari

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 03:28:03