执行1对N映射的连接

时间:2015-09-29 10:57:44

标签: sql postgresql

假设我有这些模型:

Student:
    string name
    list<Subject> enrolledCourses

Subject:
    string name
    integer credits

我可以将其存储在数据库中的方式是:

CREATE TABLE student(
    id serial PRIMARY KEY,
    student_subject_mapping int NOT NULL,
    name character varying
);

CREATE TABLE student_subject_mapping(
    id serial PRIMARY KEY,
    student_id int,
    subject_id int
);

CREATE TABLE subject(
    id serial PRIMARY KEY,
    name character varying,
    credits int
);

要获取学生(伪代码),我可以这样做:

var student = SELECT * FROM student WHERE id = ?id;
var subjects = SELECT * FROM subject WHERE id IN (SELECT * FROM student_subject_mapping WHERE student_id = ?student['id']);

make_student(student, subjects)

或者,我可以这样做:

var student = SELECT * FROM student INNER JOIN student_subject_mapping
                  ON student.id = student_subject_mapping.student_id INNER JOIN subject
                  ON subject.id = student_subject_mapping.subject_id;

以上将返回该类型的行(为简洁省略一些列):

+--------------+-------------------+
| student.name | subject.name      |
+--------------+-------------------+
| Rohan        | Physics           |
| Rohan        | Biology           |
| Rohan        | Computer Science  |
+--------------+-------------------+

在这种情况下,我可以从任何行获取student部分的信息,并通过迭代其他行来获取主题。这可能听起来有点复杂,但对我来说这很简单,因为我使用jooq:

Result<Record> rows = query.execute();
if (rows.size() > 0) {
    StudentRecord student = rows.get(0).into(Tables.STUDENT);
}

List<SubjectRecord> subjects = rows.stream()
                                   .map(x -> x.into(Tables.SUBJECT))
                                   .collect(Collectors.toList());

但我不确定这是否是正确的方法。如果subject类似于:

,事情会变得更加复杂
Subject:
    string name
    list<Department> offeringDepartments

我知道这是一个不好的例子,因为subject可能不应该存储有关提供部门的信息,但请一起玩,因为我相信你可以看到模型可能有多层嵌套。在这种情况下,不仅我的查询变得复杂,我有类型的行:

+--------------+-------------------+------------------+
| student.name | subject.name      | departments.name |
+--------------+-------------------+------------------+
| Rohan        | Physics           | Science          |
| Rohan        | Biology           | Science          |
| Rohan        | Computer Science  | EECS             |
| Rohan        | Computer Science  | IT               |
+--------------+-------------------+------------------+

在前一种情况下,我可以从第一行本身获取有关student的信息,但我无法知道获取主题行信息的边界是[0,1]; [1,2]和[2,4](除了可能是一些额外的SQL魔法)。

那么,您建议我遵循哪种方法?或者还有第三种更好的选择吗?

1 个答案:

答案 0 :(得分:0)

我认为在单个查询中检索学生和主题是完全合适的。一些框架(ActiveRecord)确实可以很容易地像这样工作。用例通常是您从表和一堆“父”表中进行选择的地方,但是对于单个子关系来说,这样做没有任何问题。

它会变得棘手的是多个子关系,例如,如果您正在检索产品及其关联的子订单和库存水平,其中100个订单和10个库存水平等于1,000行且有大量冗余数据(所谓的一些圈子是“裂口陷阱”)。在这种情况下,您需要使用两个或三个查询来返回结果。