在线考试多次作答后分数重复,需实现旧分数覆盖更新
解决在线考试重复存储学生作答记录的问题
我开发了一款带有在线考试功能的校园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方法批量插入记录,每次提交都会新增数据,没有判断学生是否已经存在对应题目的作答记录,导致重复存储。
解决方案
提供两种可行方案,根据实际场景选择:
方案一:利用数据库唯一索引 + 批量更新(推荐)
这种方案更高效,仅更新已有记录,新增不存在的记录,适合大多数场景。
设置数据库联合唯一索引
给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);修改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; } }
方案二:先删除旧记录再插入新记录(简单直接)
如果不需要保留历史修改痕迹,且学生是一次性提交所有题目答案,可以先删除该学生的所有旧作答记录,再插入新记录。
在Controller中添加删除逻辑
在调用add方法前,先删除当前学生的旧记录:// 在$this->onlineexamresult_model->add($save_result);之前添加 $studentId = $this->input->post('onlineexam_student_id'); $this->onlineexamresult_model->deleteStudentResults($studentId);在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
相关产品推荐
相关产品推荐

