将 n 个问题分发给 m 个学生,所有学生的问题相同但顺序不同

Bla*_*ard 2 mysql phpmyadmin

我正在使用 PHP 和 MySQL 构建一个在线考试平台。我对数据库有以下要求:

  • 试题为 MCQ 类型,共有 20 道题。
  • 有30名学生参加考试。

有没有办法让这 30 名学生按不同的顺序收到 20 个问题?例如,学生 A 的问题 1 将是学生 B 的问题 16?

Vér*_*ace 5

TL; DR - 最终想出了如何在一次通过中做到这一点 - 答案在最后。我保留了原始答案,以便这里的人们可以从我自己的(吱吱作响的 ;-) )思维过程中学习!

更新:下面提供了一个替代答案。

这被证明是相当棘手的。为了解决这个问题,我做了以下(见小提琴here

我创建了表:

CREATE TABLE test_question
(
  tq_id INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY,
  q_text VARCHAR (256) NOT NULL
);
Run Code Online (Sandbox Code Playgroud)

就本演示而言,问题的文本并不重要:

INSERT INTO test_question (q_text) VALUES ('q1');
INSERT INTO test_question (q_text) VALUES ('q2');
INSERT INTO test_question (q_text) VALUES ('q3');
INSERT INTO test_question (q_text) VALUES ('q4');
INSERT INTO test_question (q_text) VALUES ('q5');
INSERT INTO test_question (q_text) VALUES ('q6');
Run Code Online (Sandbox Code Playgroud)

然后分配的问题部分:

CREATE TABLE assigned_question
(
  aq_id INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY,
  st_id INTEGER,  -- Student id
  qu_id INTEGER,  -- Question id
  q_text VARCHAR (256) -- text of the question
);
Run Code Online (Sandbox Code Playgroud)

然后,为了生成一个随机的 student_question 组合列表,即填充assigned_question 表,我使用了两个RECURSIVE WITHs(或CTEs):

INSERT INTO assigned_question (st_id, qu_id)
(
  WITH RECURSIVE student AS (
    SELECT 1 AS s_no
    UNION ALL
    SELECT s_no + 1 AS value
    FROM student
    WHERE student.s_no <= 5 -- 6 students
),
question AS (
    SELECT 1 AS q_no
    UNION ALL
    SELECT q_no + 1 AS value
    FROM question
    WHERE question.q_no <= 4 -- 5 questions
)
SELECT * FROM student
CROSS JOIN question
ORDER BY s_no, RAND()
);
Run Code Online (Sandbox Code Playgroud)

这里重要的一点是RAND()对结果集的问题部分进行随机排序的函数。您为学生分配一个随机的学生编号,然后告诉他们根据他们的 ID 编号从主表中进行选择。

现在,我不能保证每个学生都会有一套完全不同的问题,但他们中的大多数应该有不同的问题清单——如果你不允许他们互相交谈并告诉他们回答所有问题问题,按顺序,即使答案是空白,你也有一种合理的方法来检查他们是否在回答他们自己的问题。少数有匹配问题列表的学生无论如何都无法交流!

然后,将这个assigned_question 表连接到test_question 表,这样:

SELECT aq.aq_id, aq.st_id, aq.qu_id, tq.q_text
FROM assigned_question aq
JOIN test_question tq 
  ON aq.qu_id = tq.tq_id;
Run Code Online (Sandbox Code Playgroud)

结果(示例,请参阅小提琴以获取完整结果):

aq_id   st_id   qu_id   q_text
    1       1       2       q2
    2       1       5       q5
 ... results omitted for brevity
    6       2       4       q4
    7       2       3       q3
Run Code Online (Sandbox Code Playgroud)

因此,我们可以看到学生 1 必须先回答问题 2 和 5,而学生 2 将首先回答问题 4 和 3(完整结果如下)。

这可能是您在纯 MySQL 中所能做的最好的事情。

更新 - 单程查询:

我已经“摆弄”了更多(请原谅双关语!:-))实际上可以一次完成,如下所示:

(旁白:另请参阅这个更优雅的小提琴,它更类似于我的第二个答案)。

INSERT INTO assigned_question (st_id, qu_id, q_text)
SELECT s_no, q_no, q_text FROM
(
SELECT * FROM
(
  WITH RECURSIVE student AS (
    SELECT 1 AS s_no
    UNION ALL
    SELECT s_no + 1 AS value
    FROM student
    WHERE student.s_no <= 5 -- students - 6 questions
),
question AS (
    SELECT 1 AS q_no
    UNION ALL
    SELECT q_no + 1 AS value
    FROM question
    WHERE question.q_no <= 4 -- students - 5 questions
)
SELECT * FROM student
CROSS JOIN question
ORDER BY s_no, RAND()
) AS z
JOIN test_question tq ON z.q_no = tq.tq_id) AS a
ORDER BY a.s_no, RAND();
Run Code Online (Sandbox Code Playgroud)

结果:

aq_id   st_id   qu_id   q_text
    1       1       3       q3
    2       1       2       q2
    ... results again omitted for brevity...
    6       2       4       q4
    7       2       3       q3
Run Code Online (Sandbox Code Playgroud)

因此,学生 1 将首先回答问题 3 和 2,学生 2 将首先回答问题 4 和 3。在检查时,我用最后一个小提琴生成的结果表明,6 名学生中的每个人的前 2 个问题都不同,因此它似乎运行良好。但是请记住,该RAND()功能并不能保证这一点,并且可能会有学生的问题顺序相同 - 但对于 30 名学生和 20 个问题,这是极不可能的!哼!ps 欢迎来到论坛!:-)

替代答案:

你可以做的是“错开”(见下面的结果解释)学生提出的问题。所有必要的 DDL 表构造和 DML 数据都在帖子的底部 - 我将只介绍下面 SQL 的“核心”。小提琴可用在这里

SELECT
  ROW_NUMBER() OVER () AS "Num",  -- all records - not strictly necessary
  t.s_id,
  CONCAT
  (
    t.q_id, 
    CASE
      WHEN t.q_id % 100 BETWEEN 11 and 13 THEN "th"
      WHEN t.q_id % 10  = 1               THEN "st"
      WHEN t.q_id % 10  = 2               THEN "nd"
      WHEN t.q_id % 10  = 3               THEN "rd"
      ELSE "th"
    END
  ) AS "Q_ID_2",
  ((s_id + q_id - 2 ) % (SELECT COUNT(*) FROM question )) + 1 AS "Question_id",
  q.question_text
FROM test t
JOIN question q ON ((s_id + q_id - 2 ) % (SELECT COUNT(*) FROM question )) + 1  = q.question_id
ORDER BY s_id, q_id; 
Run Code Online (Sandbox Code Playgroud)

这里的关键位是模数 (%) 运算符并添加 s_id 和 q_id。CONCAT获得序数的整个过程只是在炫耀!:-) 在这里找到序数片段。

结果:

Num     s_id    Q_ID_2  Question_id     question_text
  1        1       1st            1     question_1_txt
  2        1       2nd            2     question_2_txt
Run Code Online (Sandbox Code Playgroud)

所以,如我们所见,第一个学生的第一个问题是 question_id 1,即 question_1_text。“那又怎样? ”,你可能会说,这不是最初的问题!

但是,当我们查看第二个学生的问题时,我们会得到:

Num     s_id    Q_ID_2  Question_id     question_text
  9        2       1st            2     question_2_txt
 10        2       2nd            3     question_3_txt
Run Code Online (Sandbox Code Playgroud)

现在,第二个学生的第一个问题是 question_id 2,即问题 2 与他邻居的第一个问题不同 - 这是 OP 要求的!

很酷的是,如果我们看一下学生 4 和 5 之间的“接口”,我们会得到:

Num     s_id    Q_ID_2  Question_id     question_text
 31        4       7th            2     question_2_txt
 32        4       8th            3     question_3_txt
 33        5       1st            5     question_5_txt
 34        5       2nd            6     question_6_txt
Run Code Online (Sandbox Code Playgroud)

学生 4 回答的最后一个问题是 2 和 3 - 即问题已经“环绕” - 所以没有学生错过任何问题。

这实际上只是一种智力练习。对于(接近)真正的随机性,我的第一个答案更好——我能想到的唯一优势是它可以更容易地分发试卷——所有监考人员(监督考试的人)要做的就是将一张纸移到回到每张新办公桌前。希望你喜欢!:-)

============================== DDL 和 DML ================

CREATE TABLE question
(
  question_id INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY,
  question_text VARCHAR (30) NOT NULL
);

INSERT INTO question (question_text) VALUES ('question_1_txt');
INSERT INTO question (question_text) VALUES ('question_2_txt');
INSERT INTO question (question_text) VALUES ('question_3_txt');
INSERT INTO question (question_text) VALUES ('question_4_txt');
INSERT INTO question (question_text) VALUES ('question_5_txt');
INSERT INTO question (question_text) VALUES ('question_6_txt');
INSERT INTO question (question_text) VALUES ('question_7_txt');
INSERT INTO question (question_text) VALUES ('question_8_txt');



CREATE TABLE student
(
  student_id INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY,
  student_name VARCHAR (50) NOT NULL,
  UNIQUE (student_name)
);

INSERT INTO student (student_name) VALUES ('student_1');
INSERT INTO student (student_name) VALUES ('student_2');
INSERT INTO student (student_name) VALUES ('student_3');
INSERT INTO student (student_name) VALUES ('student_4');
INSERT INTO student (student_name) VALUES ('student_5');
INSERT INTO student (student_name) VALUES ('student_6');


CREATE TABLE test
(
  test_id INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY,
  s_id INTEGER NOT NULL,
  q_id INTEGER NOT NULL
);


INSERT INTO test (s_id, q_id) 
SELECT * FROM (
SELECT s.student_id, tab1.question_id
FROM student s
CROSS JOIN (SELECT q2.question_id FROM question q2)
AS tab1) AS tab2 
ORDER BY student_id, question_id;
Run Code Online (Sandbox Code Playgroud)