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

在线考试多次作答后分数重复,需实现旧分数覆盖更新

解决在线考试重复存储学生作答记录的问题

我开发了一款带有在线考试功能的校园Web应用,允许学生多次参加考试。当前存在问题:当学生两次及以上作答并提交时,数据库中的作答记录会重复存储,而非更新原有记录。需要实现学生再次作答时,旧的作答记录被覆盖替换,而非重复存储。

重复的在线考试作答记录

现有代码

Controller 代码

public function save()
{
    if ($this->input->server('REQUEST_METHOD') == 'POST') {
        $total_rows = $this->input->post('total_rows');

        if (!empty($total_rows)) {
            $save_result = array();
            foreach ($total_rows as $row_key => $row_value) {
                if (($_POST['question_type_' . $row_value]) == "singlechoice") {
                    if (isset($_POST['radio' . $row_value])) {
                        $save_result[] = array(
                            'onlineexam_student_id' => $this->input->post('onlineexam_student_id'),
                            'onlineexam_question_id' => $this->input->post('question_id_' . $row_value),
                            'select_option' => $_POST['radio' . $row_value],
                            'attachment_name' => "",
                            'attachment_upload_name' => "",
                        );
                    }
                } elseif (($_POST['question_type_' . $row_value]) == "true_false") {
                    if (isset($_POST['radio' . $row_value])) {
                        $save_result[] = array(
                            'onlineexam_student_id' => $this->input->post('onlineexam_student_id'),
                            'onlineexam_question_id' => $this->input->post('question_id_' . $row_value),
                            'select_option' => $_POST['radio' . $row_value],
                            'attachment_name' => "",
                            'attachment_upload_name' => "",
                        );
                    }
                } elseif (($_POST['question_type_' . $row_value]) == "multichoice") {
                    if (isset($_POST['checkbox' . $row_value])) {
                        $save_result[] = array(
                            'onlineexam_student_id' => $this->input->post('onlineexam_student_id'),
                            'onlineexam_question_id' => $this->input->post('question_id_' . $row_value),
                            'select_option' => json_encode($_POST['checkbox' . $row_value]),
                            'attachment_name' => "",
                            'attachment_upload_name' => "",
                        );
                    }
                } elseif (($_POST['question_type_' . $row_value]) == "descriptive") {
                    if (isset($_POST['answer' . $row_value]) || (isset($_FILES["attachment" . $row_value]) && !empty($_FILES["attachment" . $row_value]['name']))) {
                        $inst_array = array(
                            'onlineexam_student_id' => $this->input->post('onlineexam_student_id'),
                            'onlineexam_question_id' => $this->input->post('question_id_' . $row_value),
                            'select_option' => $_POST['answer' . $row_value],
                        );

                        $file_name = "";
                        $upload_file_name = "";
                        if (isset($_FILES["attachment" . $row_value]) && !empty($_FILES["attachment" . $row_value]['name'])) {
                            $file_name = $_FILES["attachment" . $row_value]["name"];
                            $fileInfo = pathinfo($_FILES["attachment" . $row_value]["name"]);
                            $upload_file_name = time() . uniqid(rand()) . '.' . $fileInfo['extension'];
                            move_uploaded_file($_FILES["attachment" . $row_value]["tmp_name"], "./uploads/onlinexam_images/" . $upload_file_name);
                        }
                        $inst_array['attachment_name'] = $file_name;
                        $inst_array['attachment_upload_name'] = $upload_file_name;

                        $save_result[] = $inst_array;
                    }
                }
            }

            $this->onlineexamresult_model->add($save_result);
            $this->onlineexam_model->updateExamResult($this->input->post('onlineexam_student_id'));
            redirect('user/onlineexam', 'refresh');
        }
    } else {
        // GET 请求处理逻辑
    }
}

Model 代码

public function add($data_insert)
{
    $this->db->trans_begin();

    if (!empty($data_insert)) {
        $this->db->insert_batch('onlineexam_student_results', $data_insert);
    }

    if ($this->db->trans_status() === false) {
        $this->db->trans_rollback();
        return false;
    } else {
        $this->db->trans_commit();
        return true;
    }
}

问题根源

当前代码使用insert_batch方法批量插入记录,每次提交都会新增数据,没有判断学生是否已经存在对应题目的作答记录,导致重复存储。

解决方案

提供两种可行方案,根据实际场景选择:


方案一:利用数据库唯一索引 + 批量更新(推荐)

这种方案更高效,仅更新已有记录,新增不存在的记录,适合大多数场景。

  1. 设置数据库联合唯一索引
    给onlineexam_student_results表添加onlineexam_student_id和onlineexam_question_id的联合唯一索引,确保同一学生同一题目只能有一条记录:

    ALTER TABLE onlineexam_student_results 
    ADD UNIQUE KEY idx_student_question (onlineexam_student_id, onlineexam_question_id);
    
  2. 修改Model的add方法
    替换默认的insert_batch为支持INSERT ... ON DUPLICATE KEY UPDATE的自定义SQL,实现批量插入或更新:

    public function add($data_insert)
    {
        $this->db->trans_begin();
    
        if (!empty($data_insert)) {
            $table = 'onlineexam_student_results';
            // 获取所有字段名
            $fields = array_keys($data_insert[0]);
            $fieldStr = implode(', ', $fields);
    
            // 生成批量插入的VALUES部分
            $values = [];
            foreach ($data_insert as $row) {
                $rowValues = [];
                foreach ($fields as $field) {
                    $rowValues[] = $this->db->escape($row[$field]);
                }
                $values[] = '(' . implode(', ', $rowValues) . ')';
            }
            $valuesStr = implode(', ', $values);
    
            // 生成重复键时的更新语句(跳过唯一索引字段)
            $updateClauses = [];
            foreach ($fields as $field) {
                if (!in_array($field, ['onlineexam_student_id', 'onlineexam_question_id'])) {
                    $updateClauses[] = "$field = VALUES($field)";
                }
            }
            $updateStr = implode(', ', $updateClauses);
    
            // 执行完整SQL
            $sql = "INSERT INTO $table ($fieldStr) VALUES $valuesStr ON DUPLICATE KEY UPDATE $updateStr";
            $this->db->query($sql);
        }
    
        if ($this->db->trans_status() === false) {
            $this->db->trans_rollback();
            return false;
        } else {
            $this->db->trans_commit();
            return true;
        }
    }
    

方案二:先删除旧记录再插入新记录(简单直接)

如果不需要保留历史修改痕迹,且学生是一次性提交所有题目答案,可以先删除该学生的所有旧作答记录,再插入新记录。

  1. 在Controller中添加删除逻辑
    在调用add方法前,先删除当前学生的旧记录:

    // 在$this->onlineexamresult_model->add($save_result);之前添加
    $studentId = $this->input->post('onlineexam_student_id');
    $this->onlineexamresult_model->deleteStudentResults($studentId);
    
  2. 在Model中添加删除方法

    public function deleteStudentResults($studentId)
    {
        $this->db->where('onlineexam_student_id', $studentId);
        $this->db->delete('onlineexam_student_results');
    }
    

方案对比

  • 方案一:性能更优,仅修改必要数据,支持增量提交(如中途保存部分题目),但依赖数据库的ON DUPLICATE KEY UPDATE语法(MySQL/MariaDB支持)。
  • 方案二:实现简单,无需修改数据库结构,但会删除所有旧记录,适合一次性提交所有答案的场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 16:36:07