【问题标题】:Is there a shorter alternative to my MySql query?我的 MySql 查询是否有更短的替代方法?
【发布时间】:2018-09-15 11:07:11
【问题描述】:

我是 Java 的学生,也学习 SQL。在一节课中,我们看到了一个示例数据库草图,以及一个在该问题中复制的查询。

我用MySql做了一个例子,它有三个表,

CREATE TABLE `employed` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(45) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;

CREATE TABLE `department` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(45) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;

CREATE TABLE `employees_departments` (
`employed_id` int(11) NOT NULL,
`department_id` int(11) NOT NULL,
PRIMARY KEY (`employed_id`,`department_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

就业满了

(1 'Karl'), (2 'Bengt'), (3 'Adam'), (4 'Stefan')

部门满了

(4, 'HR'), (5, 'Sälj'), (6, 'New departm')

employees_departments 已满

1 4
2 5
3 4

所以“Stefan”没有部门,“新部门”没有雇员。

我想要一个查询,它可以为员工及其所有部门、没有部门的员工和没有员工的部门提供信息。我找到了这样的解决方案:

select A.name, C.name from employed A
left join employees_departments B on (A.id=B.employed_id)
left join department C on (B.department_id = C.id)
union
select C.name, A.name from department A
left join employees_departments B on (A.id=B.department_id)
left join employed C on (B.employed_id = C.id)

如果有一个简短的查询就可以了...

另外,我在没有外键约束的情况下做了这个,因为我想在这个例子中尽可能简单。

问候

【问题讨论】:

  • “我的 MySql 查询是否有更短的替代方法”我在这个问题中没有看到 SQL 查询。
  • select A.name, C.name fromemployee A left join employees_departments B on (A.id=B.employees_id) left join department C on (B.department_id = C.id) union select C .name, A.name 从部门 A 左加入employees_departments B on (A.id=B.department_id) 左加入受雇C on (B.employees_id = C.id)
  • 嗨。请阅读minimal reproducible example 并采取行动。注意约束允许在编写/优化查询时使用其他不可用的选项。请通过编辑您的帖子来澄清,而不是通过 cmets。了解什么是左连接返回:行上的内连接加上由空值扩展的不匹配的左表行。了解什么是完全连接返回:行内连接加上由空值扩展的不匹配行。 (通常,左连接在联合右连接上。)(不受 MySQL 支持。)
  • 您的问题是什么?您是否寻求查询?还是更好的?比什么好? “给”“没有部门的员工和没有员工的部门”不清楚——怎么给?请编辑您的帖子以使其清晰。
  • 我进行了更正。我的评论:从“绿色”答案可以得出结论,我的问题实际上是两个问题——关于 MySql 中的(外部)连接以及为什么要使用外键约束。很抱歉,如果这种模糊性令人不安。

标签: mysql many-to-many union outer-join linktable


【解决方案1】:

MySQL 不支持 FULL OUTER 连接操作。

我们可以通过组合两个集合来模拟它...一个 OUTER JOIN 的结果和一个反 JOIN 的结果。

(
  SELECT ee.name        AS employed_name
       , dd.name        AS department_name 
    FROM employed ee
    LEFT
    JOIN employees_departments ed
      ON ed.employed_id = ee.id
    LEFT
    JOIN department dd
      ON dd.id = ed.department_id
)
UNION ALL 
(
  SELECT nn.name        AS employed_name
       , nd.name        AS department_name
    FROM department nd
    LEFT
    JOIN employees_departments ne
      ON ne.deparment_id = nd.id
    LEFT
    JOIN employeed nn
      ON nn.id = nd.employee_id
   WHERE nn.id IS NULL
)

第一个 SELECT 返回所有 employed 名称,以及匹配的 department 名称,包括没有 departmentemployed

第二个 SELECT 只返回在 employed 中没有匹配行的 department 名称。

两个 SELECT 的结果使用 UNION ALL 集合运算符组合/连接。 (UNION ALL 操作避免了使用 UNION 集合操作符强制执行的可能代价高昂的“使用文件排序”操作。

这是返回这些行的最短查询模式。


我们可以使 SQL 更短一些。例如,如果我们在 employeed_departmentemployed 之间有一个外键关系(原始帖子中没有表明这种关系是强制执行的,所以我们不假设存在这种关系)......但如果那是强制执行,那么我们可以从第二个 SELECT 中省略 employed

UNION ALL
(
  SELECT NULL           AS employed_name
       , nd.name        AS department_name
    FROM department nd
    LEFT
    JOIN employees_departments ne
      ON ne.deparment_id = nd.id
   WHERE ne.department_id IS NULL
)

有了合适的可用索引,这将为我们提供最有效的访问计划。

是否有更短的 SQL 可以返回等效结果?如果有,它可能不会像上述那样高效。


【讨论】:

  • 非常感谢。我实施了您的第一个示例,具有“成本”增益(从 19.2 到 17.4),然后重新设计了具有 FK 约束的表格,然后获得了进一步的成本增益(从 17.4 到 15.2)。我纠正了一些代码错误。
猜你喜欢
  • 1970-01-01
  • 2020-05-21
  • 2019-09-14
  • 1970-01-01
  • 2015-02-12
  • 2011-02-28
  • 2018-03-10
  • 2022-06-15
  • 1970-01-01
相关资源
最近更新 更多