如何使这个子查询工作?

时间:2010-12-30 03:36:12

标签: sql database mysql

我试图在一个查询中执行此操作。我几天前问了一个类似的问题,但我个人的要求已经改变了。

我有一个游戏类型的网站,用户可以参加“课程”。我的数据库中有三个表。

我正在使用MySQL。我有四张桌子:

  • hl_classes(int id,int professor, varchar类,文本描述)
  • hl_classes_lessons(int id,int class_id,varchar lessonTitle,varchar lexiconLink, text lessonData)
  • hl_classes_answers (int id,int lesson_id,int student, text submit_answer,int percent)

hl_classes存储网站上的所有类。

课程是每节课的个别课程。课程可以有无限的课程。每节课都有特定的术语。

hl_classes_terms存储所有术语的列表,当前术语的字段为active ='1'。

当用户提交他们对课程的答案时,它存储在hl_classes_answers中。用户只能回答一次课程。课程必须按顺序回答。所有用户都参加所有“课程”。

我要做的是为每个班级的每个用户抓住下一课。当用户开始他们在第1学期时。当他们完成每个课程的所有10个课程时,他们继续到第2学期。当他们完成每个课程的第20课时,他们继续到第3个学期。让我们说我们知道用户的术语是通过PHP变量$ term。

所以这是我的查询,我目前正在努力按摩,但它不起作用。特别是因为在WHERE子句中未知hC.id

SELECT hC.id, hC.class, (SELECT MIN(output.id) as nextLessonID
  FROM ( SELECT id, class_id
   FROM hl_classes_lessons hL
   WHERE hL.class_id = hC.id
   ORDER BY hL.id
   LIMIT $term,10 ) as output
  WHERE output.id NOT IN (SELECT lesson_id FROM hl_classes_answers WHERE student = $USER_ID)) as nextLessonID
FROM hl_classes hC

这个查询背后的逻辑首先是每个类;选择当前用户所在的术语中的所有课程。从中挑选出用户已经完成的课程,并获取尚未完成的课程的MINIMUM ID。这将是用户必须要做的教训。

我希望我的问题足够明确。

2 个答案:

答案 0 :(得分:2)

我的假设是你想要一个查询,告诉特定学生在给定学期的下一课是什么,如果在该学期没有该学生的其他课程,则为null。结果应该是一行或null。

为了以任何效率(以及恕我直言,理智)来做到这一点,您需要首先重新审视您的表格结构和对数据的假设。我假设您提供的表格结构以及您如何描述课程编号,例如,课程1,课程1,2,3,...,10,11,... 20,21 ,...,30,然后是第2课,第1课30课,然后是第3课,第1课...... 30等。此外,每节课1-10节对应第1期,第11-20期,到第2学期,第21到30学期到第3学期。最后,学期按顺序完成 - 第3课第10课在第1课第11课之前完成。

首先,我建议一个唯一的id字段(可能是一个自动增量)和一个单独的class_num,而不是使用你的类号作为唯一标识符和订购号(第1类发生在第2类之前等)。订购号的字段。 (对于类表而言,这对于课程表而言不如下文所述。)

接下来,类似地,课程应该获得与其课程编号字段分开的唯一ID字段。 id就是PK。这个唯一的ID是大大简化您想要的查询以及您可能需要的任何其他查询所必需的。没有它,你正在处理一个双字段复合键,使许多连接和子查询噩梦复杂。您可能希望在class_id和lesson_num上有一个额外的唯一索引,以便不为课程重用课程编号。此外,此表应包含为特定类分配特定课程的term_num(或term_id)。这将使您不必计算使用过于复杂的MOD公式的课程。那太过分了。只需将术语编号与课程信息一起存储,您就可以随意组织术语。

接下来,答案表的id字段应该是唯一的自动增量。如果它很重要,您可能还需要一个关于lesson_id和student_id 的唯一索引(尽管这意味着不会重拍或重新覆盖)

所以我现在有:

  • hl_classes(int id,int class_num,professor,class_name,description)PK:id,autoinc
  • hl_classes_lessons(int id,int class_id,int lesson_num,int term_num,l_title,l_link,l_data)PK:id,autoinc;唯一键:class_id,lesson_num
  • hl_classes_answers(int id,int lesson_id,int student,ans,pct)PK:id,autoinc;唯一键:lesson_id,学生

有了这个,我想出了:

select hC.id as next_class_id, hL.id as next_lesson_id, hC.class as next_class, hL.term_num as term_num, hC.class_num as next_class_num, hL.lesson_num AS next_lesson_num
from hl_classes hC
left join hl_classes_lessons hL on hL.class_id = hC.id
where hL.term_num = $TERM_NUM
and hL.id not in (
    select hA.lesson_id
    from hl_classes_answers hA
    where student = $USER_ID
)
order by hC.class_num, hL.lesson_num
limit 1;

这将返回一行,其中包含有关该学生的下一个课程的相关信息,给定该术语或所有空值。请注意,ID不是用于显示,因为它们可以是任何数字。您将显示_num字段。

答案 1 :(得分:0)

我不确定您希望最终结果是什么样的,以及为什么您在查询中有LIMIT $ term,但如果您想为用户获取所有课程和下一课(如果可用)可以用这个:

SELECT c.*, l.*
FROM hl_classes c JOIN (
        SELECT l.class_id, MIN(l.id) NextLessonID
        FROM hl_classes_lessons l LEFT JOIN (
                SELECT sca.class_id, MAX(sca.lesson_id) MaxID
                FROM hl_classes_answers sca
                WHERE sca.student = $USER_ID
                GROUP BY sca.class_id
            ) cm ON (l.class_id = cm.class_id AND l.id > cm.MaxID) OR cm.class_id IS NULL
        GROUP BY l.class_id
    ) nid ON c.id = nid.class_id
    JOIN hl_classes_lessons l ON c.id = l.class_id AND l.id = nid.NextLessonID