【问题标题】:How to make this sub-sub-query work?如何使这个子子查询工作?
【发布时间】:2010-12-30 03:36:11
【问题描述】:

我正在尝试在一个查询中执行此操作。几天前我问了一个类似的问题,但我的个人要求发生了变化。

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

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

  • hl_classes (int id, int 教授, varchar 类,文本描述)
  • hl_classes_lessons(int id,int 类 ID,varchar 课程标题,varchar lexiconLink, 文本课程数据)
  • hl_classes_answers (int id, int course_id, int 学生, 文本 submit_answer,int 百分比)

hl_classes 存储网站上的所有类。

课程是每个班级的单独课程。一个班级可以有无限的教训。每节课都有特定的学期。

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

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

我要做的是为每个用户在每个班级中学习下一课。当用户开始时,他们在第 1 学期。当他们完成每节课的所有 10 节课时,他们进入第 2 学期。当他们完成每节课的第 20 课时,他们进入第 3 学期。假设我们知道用户是由 PHP 变量 $term 输入。

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

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。这将是用户必须做的一课。

我希望我的问题已经足够清楚了。

【问题讨论】:

  • 定义“不起作用”。查询是否产生错误?结果有错吗?如果是这样,它们哪里错了?它们是什么,你期望它们是什么?
  • 我确实提到它没有运行并且我得到一个错误,但我得到的具体错误是:查询错误:'where 子句'中的未知列'hC.id'。我知道我可以选择 SELECT *, (SELECT id FROM hl_classes_lessons WHERE class_id = hC.id LIMIT 1) as courseID FROM hl_classes hC。但这不起作用,我认为这是因为我更深入了一层。所以我一直在绞尽脑汁想弄清楚如何让它发挥作用。
  • 您收到该特定错误是因为 hC 未出现在该子查询的 FROM 子句中(或以其他方式可访问)。
  • 您能否发布hl_classes_terms 表的定义?另外,请发布您希望结果的样子。
  • @robertb 不适用于hl_classes_terms 表...

标签: sql database mysql


【解决方案1】:

我的假设是,您需要一个查询来告诉特定学生在给定学期的下一课是什么,如果该学生在该学期没有其他课程,则为 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 课之前完成。

首先,我建议不要同时使用您的班级编号作为唯一标识符和订购号(班级 1 发生在班级 2 之前,等等),我建议使用唯一的 id 字段(可能是自动增量)和单独的 class_num订购号字段。 (与 classes 表相比,这对于 classes 表来说没有那么重要,如下所述。)

接下来,同样地,课程应该获得一个与课程编号字段分开的唯一 ID 字段。 id 将是 PK。这个唯一的 id 是极大地简化您想要的查询以及您可能需要的任何其他查询所必需的。没有它,您将处理一个包含两个字段的复合键,这会使许多连接和子查询变得异常复杂。您可能希望在 class_id 和 course_num 上有一个额外的唯一索引,这样课程编号就不会被重新用于课程。此外,此表应包含为特定班级的特定课程分配的 term_num(或 term_id)。这将使您不必使用过于复杂的 MOD 公式计算课程的术语。那将是矫枉过正。只需将术语编号与课程信息一起存储,您就可以根据需要组织术语。

接下来,answers 表的 id 字段应该是唯一的自动增量。如果它很重要,您可能还需要一个关于课程 ID 和学生 ID 的唯一索引(尽管这意味着要么不重考,要么重考覆盖)

所以我现在有:

  • hl_classes (int id, int class_num,professor, class_name, description) PK: id, autoinc
  • hl_classes_lessons (int id, int class_id, int course_num, int term_num, l_title, l_link, l_data) PK: id, autoinc;唯一键:class_id、课程编号
  • hl_classes_answers (int id, int course_id, int student, ans, pct) PK: id, autoinc;唯一键:课程 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 字段。

【讨论】:

  • 要按所有术语的正确顺序获取所有课程和课程的列表,您将执行相同的 select、from 和 left join,完全删除 where 子句,删除限制和顺序by 将是 hL.term_num、hC.class_num、hL.lesson_num。
  • 非常感谢,这很有意义并回答了所有问题。当我了解规范化数据库时,我了解到您不希望拥有比您必须维护的更多信息。因此,为什么我不存储“术语”,因为我认为每 10 个属于他们各自的第 n 个术语,这应该很容易计算。
  • @Josh 不客气。规范化后你学到的下一件事是什么时候打破这些规则。很多时候,您采用规范化设计并将其非规范化以提高性能或方便性。你只需要深谋远虑,这样你就不会陷入麻烦。关于术语,我认为将术语放在那里实际上是对数据进行规范化。您要避免的事情是一个数据元素意味着很多事情。您的课程编号定义了 3 件事:课程 ID、课程顺序和术语(通过算法操作)。至少 1 太多了。
【解决方案2】:

我不确定您希望最终结果是什么样子,以及为什么您的查询中有 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

【讨论】:

    猜你喜欢
    • 2012-08-31
    • 2015-02-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-21
    • 1970-01-01
    • 1970-01-01
    • 2018-11-25
    相关资源
    最近更新 更多