【问题标题】:Double FULL OUTER JOIN in MySQLMySQL中的双重全外连接
【发布时间】:2017-05-24 03:57:39
【问题描述】:

我完全了解这个问题并回答: https://stackoverflow.com/a/4796911/2307520 但这是一个有点不同的问题!

是的,我了解他们如何使用联合和两个左连接在 MySQL 中模拟简单的 FULL OUTER JOIN,但我的情况有点复杂(3 个表而不是 2 个),我很难找到修改该解决方案的方法满足我的需要。

这是简化的数据库架构:

 CREATE TABLE item (`i_id` int, `i_name` varchar(70), b_id int null , s_id int null , PRIMARY KEY (i_id));
 CREATE TABLE box (`b_id` int, `b_name` varchar(70), s_id int null , PRIMARY KEY (b_id) );
 CREATE TABLE shelf (`s_id` int, `s_name` varchar(70) , PRIMARY KEY (s_id));
 INSERT INTO shelf VALUES(1,'shelf1');
 INSERT INTO shelf VALUES(2,'shelf2');
 INSERT INTO shelf VALUES(3,'empty shelf');
 INSERT INTO box VALUES(1,'box in s1 with item in it',1);
 INSERT INTO box VALUES(2,'box without shelf',NULL);
 INSERT INTO box VALUES(3,'empty box',2);
 INSERT INTO item VALUES(1,'item a',1,NULL);
 INSERT INTO item VALUES(2,'item b',1,NULL);
 INSERT INTO item VALUES(3,'item c',2,NULL);
 INSERT INTO item VALUES(4,'item in a shelf',NULL,1);
 INSERT INTO item VALUES(5,'item without location',NULL,NULL);

item 可以放在盒子里,也可以单独放在架子上,或者两者都没有(未定义)。框可以在架子上或未定义。

理想情况下,我会使用这样的 SQL 查询:(如果我在 MySQL 中有 FULL OUTER JOIN)

SELECT *,coalesce(item.b_id,box.b_id) as b_id,coalesce(item.s_id,box.s_id,shelf.s_id) as s_id
FROM item
FULL OUTER JOIN box ON box.b_id=item.i_id
FULL OUTER JOIN shelf ON (shelf.s_id=item.s_id) OR (shelf.s_id=box.s_id)
WHERE ...

这将列出符合特定条件的所有条目(比如查看一个盒子在哪个架子上(如果有的话)以及里面的所有物品,或其他任何东西)

【问题讨论】:

  • 我看不出架子和盒子之间连接的原因...为什么?
  • 因为空盒子可以放在架子上,在物品放在那里之前
  • 所以一个项目可能在一个盒子里,或者它可能只是坐在架子上。如果它在一个盒子里,那么这就是你确定它所在的架子的方式。那么......你想要返回的是什么?喜欢。 “我想知道我可以在哪个架子上找到每件物品”你没有提到那部分。
  • MySQL 有外连接。它没有 FULL OUTER JOIN。

标签: mysql outer-join


【解决方案1】:

我认为您可以将其从 LEFT OUTER JOIN 和一些联合中移除,例如您的示例问题。只是有点复杂,因为您有一个额外的表,因此需要考虑更多的场景。

在我看来,您可以将其简化为四种情况:

  1. 物品存在,可能在也可能不在一个盒子里,盒子可能在架子上,也可能不在架子上,但不是直接在架子上。这将考虑到所有不在盒子中且不直接放在架子上的独立物品,以及盒子中的物品,无论它们是否在架子上。
  2. 物品存在,但不在盒子里,而是直接放在架子上。
  3. 存在一个空盒子,可能在也可能不在架子上。 (如果它有一个项目,那么我们会在场景 1 中介绍它)
  4. 存在一个空架子。

/*item may or may not be in a box which may or may not be on a shelf, but the item is not directly on a shelf*/

SELECT item.i_id item.i_name, item.b_id, box.b_name, box.s_id, s.s_name FROM left outer join box on item.b_id = box.b_id left outer shelf on box.s_id = shelf.s_id and item.s_id is null

UNION ALL

/*item is directly on the shelf, but aren't in a box*/
SELECT item.i_id, item.i_name, NULL, NULL, shelf.s_id, shelf.s_name FROM item INNER JOIN shelf on item.s_id = shelf.s_id AND item.b_id IS NULL;

UNION ALL

/*empty box may or may not be on the shelf*/
SELECT NULL, NULL, box.b_id, box.b_name, shelf.s_id, shelf.s_name FROM box LEFT OUTER JOIN shelf on box.s_id = shelf.s_id WHERE box IS NOT IN (SELECT DISTINCT b_id FROM item)

UNION ALL

/*Shelfs where there are no boxes nor items sitting on them*/
SELECT NULL, NULL, NULL, NULL, shelf.s_id, shelf.s_name FROM shelf WHERE s_id NOT IN (SELECT distinct s_id FROM item) AND s_id NOT IN (SELECT DISTINCT s_id FROM box);

我认为场景 1 和 2 可以与合并相结合,但我喜欢将其分离出来的想法,因为它感觉更明确我们要追求的目标。

不过,最终这感觉有点徒劳。真的,您应该编写一条 SQL 语句来直接跟踪您所追求的内容,例如“给我一个物品所在的每个架子,无论它是否在一个盒子里”,或者“给我所有少于 20 件物品的架子”。相反,这只是吐出物品、盒子和货架的所有可能组合,如果每个下游 SQL 语句都引用它们,它们将不会执行。

【讨论】:

  • 呈现的代码有几个小问题,但核心思想帮助很大。更正的代码:pastebin.com/QBiyRrnR(我用更多的连接替换了子查询,因为连接通常性能更好)
猜你喜欢
  • 2011-12-26
  • 2015-12-14
  • 2013-03-11
  • 1970-01-01
  • 2013-12-10
  • 2021-10-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多