如何在 SQL 中实现聚合? (这与 GroupBy 无关)

Posted

技术标签:

【中文标题】如何在 SQL 中实现聚合? (这与 GroupBy 无关)【英文标题】:How to implement an Aggregation in SQL? (This is not about GroupBy) 【发布时间】:2018-06-29 03:51:22 【问题描述】:

在大学项目的范围内,我应该实现我的数据库的聚合。 我得到了一个类似于这个的实体关系模型: 现在我应该实现一个创建这样的数据库的 SQL 脚本,但是我在谷歌或其他任何地方都找不到关于这个主题的任何内容。在我教授的幻灯片中,它说

例如,为了表示关系works_on和实体集管理器之间的聚合管理,创建一个模式 管理(employee_id,branch_name,title,manager_name) Schema works_on 是多余的,前提是我们愿意为与架构管理相关的属性 manager_name 存储空值

所以我尝试将两张表放入我的 SQL 脚本中,一张名为 works-on,另一张名为 manages。在works-on中,我把jobbranchemployee的所有主键都定义为外键。在manages 中,我放入了所有这些主键,另外我放入了manager。现在的问题是,当我使用 mysql-workbench 的 Reverse-Engineer 创建数据库的 EER-Model 时,我没有从中得到任何与这种聚合有关的东西。那么我在这里做错了什么? 按照@Barmar 的要求,我刚刚写了我将用于此目的的CREATE TABLE-statements:

CREATE TABLE job
(jobid INT,
PRIMARY KEY(jobid));

CREATE TABLE employee
(employeeid INT,
PRIMARY KEY(employeeid));

CREATE TABLE branch
(branchid INT,
PRIMARY KEY(branchid));

CREATE TABLE manager
(managerid INT,
PRIMARY KEY(managerid));

CREATE TABLE works_on
(jobid INT, KEY(jobid),
branchid INT, KEY(branchid),
employeeid INT, KEY(employeeid));

CREATE TABLE manages
(jobid INT, KEY(jobid),
branchid INT, KEY(branchid),
employeeid INT, KEY(employeeid),
managerid INT, KEY(managerid));

ALTER TABLE works_on
ADD CONSTRAINT FK_workson_employee FOREIGN KEY(employeeid) REFERENCES employee(employeeid);
ALTER TABLE works_on
ADD CONSTRAINT FK_workson_branch FOREIGN KEY(branchid) REFERENCES branch(branchid);
ALTER TABLE works_on
ADD CONSTRAINT FK_workson_job FOREIGN KEY(jobid) REFERENCES job(jobid);

ALTER TABLE manages
ADD CONSTRAINT FK_manages_employee FOREIGN KEY(employeeid) REFERENCES employee(employeeid);
ALTER TABLE manages
ADD CONSTRAINT FK_manages_branch FOREIGN KEY(branchid) REFERENCES branch(branchid);
ALTER TABLE manages
ADD CONSTRAINT FK_manages_job FOREIGN KEY(jobid) REFERENCES job(jobid);
ALTER TABLE manages
ADD CONSTRAINT FK_manages_manager FOREIGN KEY(managerid) REFERENCES job(managerid);

【问题讨论】:

在您的架构中显示 CREATE TABLE 语句。 为什么有人会投票结束这个,而不告诉我这个问题有什么问题? @Barmar这不是我想要实现的实际模式,它只是一个更简单的例子。您是否需要 CREATE TABLE 语句来获取这个确切的架构,或者我可以输入我实际使用的那个吗? 您需要显示您尝试编写的任何代码;与问题相关。 @Barmar 我刚刚为这个确切的示例编写了代码并将其添加到问题中。你觉得你能帮我解决吗? 这个问题不是很清楚。该图显示了managesworks_on 之间的关系。但幻灯片没有提到类似的内容。 【参考方案1】:

您的 ER-图缺少一个重要信息:经理和新实体之间的基数,该实体由其他 4 个元素 jobemployeemanagerbranchworks-on(这个新实体由它们周围的正方形标记)。

从幻灯片上的引用我们可以推断它是0..1-relationship, 这意味着jobbranchemployee(或works-on 中的每个条目)的每个组合最多有一个manager,但不需要一个(与例如组合 em>)。

但您必须在实际任务中验证该基数。

您通常可以通过多种方式实现 ER 图,但幻灯片暗示了以下实现:

CREATE TABLE manages
( jobid INT not null,
  branchid INT not null,
  employeeid INT not null,
  managerid INT null,
  PRIMARY KEY (jobid, branchid, empoyeeid)
);

我省略了表 jobemployeemanagerbranch 的琐碎外键。

使用此实现,您不再有 works-on-relation 的显式表,就像幻灯片中的第二条语句所说的那样。它包含在manages 表中。这仅适用于0..1-relation,这就是为什么该基数是可推断的。

如果你想为works-on 保留一个表,你会使用

CREATE TABLE works_on
( jobid INT not null,
  branchid INT not null,
  employeeid INT not null,
  PRIMARY KEY (jobid, branchid, empoyeeid)
);

CREATE TABLE manages
( jobid INT not null,
  branchid INT not null,
  employeeid INT not null,
  managerid INT not null,
  PRIMARY KEY (jobid, branchid, empoyeeid),
  FOREIGN KEY (jobid, branchid, employeeid) 
    REFERENCES works_on (jobid, branchid, employeeid)
);

再次,我省略了琐碎的外键。

为了简化外键(也许是为了强调该组合被视为一个新实体),您可以按照@Barmar 的建议,在works_on-table 中添加一个额外的(通常是自动递增的)键并使用manages-table 中的这个值,虽然幻灯片在这里没有这样做。

如果您需要实现0..n-relation(多个经理可以管理特定的works-on-combination),您不能再吸收works-on-relation 中的manages-relation(所以您需要两个表),并且为了尊重n,您必须在主键PRIMARY KEY (jobid, branchid, empoyeeid, managerid) 中包含managerid(但仍需要保留FOREIGN KEY (jobid, branchid, employeeid))。

【讨论】:

非常感谢,我想这完美地回答了我的问题。 但是有一个小问题:您将“提供的冗余”理解为“没有提供,因为它是多余的”?因为我对它的理解更像是“即使它是多余的,它也提供了”。但我不是母语人士,所以我只是好奇。 还有一些小问题(对垃圾评论感到抱歉):你为什么把所有的not nulls 都放在这里?我认为主键在 SQL 中自动不能为空。 这句话应该读作“Schema works_on 是多余的,只要我们...”或“Schema works_on 是多余的,如果我们愿意存储空值”(如第一版中所做的那样)。冗余显然意味着它至少存在两次,因此该表可能存在;但是说某事也是多余的(至少对我来说)意味着你应该摆脱它。如果没有暗示,幻灯片上的下一个项目符号应该告诉您删除表格。如果要保留works-in 表(在第一个版本中),则需要确保所有记录都在两个表中!否则它不会... 是冗余的,因为works-in 中但不在manages 中的记录可以与works-inmanages 中的元组区分开来(manager 设置为@987654359 @)。这将编码一些不属于您的 ER 图的信息,因此您的 2 表实现(使用 null)只有在添加例如每次更改时更新另一个表的触发器(如果您需要管理冗余信息,通常会出现这种情况)。关于声明中明确的not nulls:它们只是为了澄清。【参考方案2】:

您需要为works_on 表提供一个主键,然后在manages 表中引用该主键,而不是直接引用employeejobbranch

CREATE TABLE works_on (
    works_on_id INT PRIMARY KEY,
    jobid INT,
    branchid INT,
    employeeid INT,
    CONSTRAINT jobid FOREIGN KEY REFERENCES job(jobid),
    CONSTRAINT branchid FOREIGN KEY REFERENCES brahc(branchid),
    CONSTRAINT employeeid FOREIGN KEY REFERENCES employee(employeeid)

);
CREATE TABLE manages (
    managerid INT,
    works_on_id INT,
    CONSTRAINT managerid FOREIGN KEY REFERENCES manager(id),
    CONSTRAINT works_on_id FOREIGN KEY REFERENCES works_on(id)
)

【讨论】:

这只是您看到的唯一方式,在managesworks_on 两个关系之间创建引用,或者您是否熟悉 Entity-Relationship-Model 中的聚合概念这就是你在 SQL 中实现这个概念的方式吗? 抱歉,我不是真正的 ERM 专家,所以我可能遗漏了一些东西。 好的。谢谢您的帮助。我将把它打开一段时间,看看是否有另一种解决方案。如果不是,我明天或某事会接受你的回答。这是一个非常烦人的问题,因为当您尝试用 Google 搜索它时,您要么得到显示在没有 SQL 实现的 ERM 方面聚合是什么的结果,要么得到关于 SQL 中聚合函数的信息......

以上是关于如何在 SQL 中实现聚合? (这与 GroupBy 无关)的主要内容,如果未能解决你的问题,请参考以下文章

在MongoDB中实现聚合函数

如何在 WebGL 中实现阴影映射?

如何在 TensorFlow 中实现递归神经网络?

在 C++ 聚合类中实现调用多路复用的优雅方式?

MySQL:在聚合函数中使用别名字段

我们如何在sql CTE中实现动态查询?