2017-01-09 69 views
-2

我是这个问题的答案完全清楚: https://stackoverflow.com/a/4796911/2307520 但是这是一个有点不同的问题!双FULL OUTER JOIN在MySQL

是的,我明白他们是如何模拟简单的FULL OUTER使用UNION在MySQL JOIN和两个左联接,但我的情况比较复杂一点(3个表,而不是仅仅2),我挣扎寻找一种方法如何修改该解决方案适合我的需求。

这被简化数据库模式: enter image description here

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); 

项可以是在一个盒子里,独立于一个货架,或者都没有(未定义)。盒子可以放在架子上或没有定义。

理想情况下,我会使用SQL查询是这样的:(如果我有FULL OUTER在MySQL 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 ... 

这将列出特定标准(所有条目看到在货架的盒子是,如果任何和所有项目,或其他任何东西)

+0

我看不出架子和箱子之间连接的原因......为什么? – markg

+0

,因为在放入物品之前,空的盒子可以放在架子里。 – David162795

+0

所以一个物品可能放在一个盒子里,或者它可能只放在架子上。如果它在一个盒子里,那么这就是你如何确定它所在的架子。那么...你想要返回什么?喜欢。 “我想知道哪个货架我可以找到每件物品”,你没有提到这一部分。 – JNevill

回答

1

我想你可以把这个关闭左外部联接和一些工会喜欢你的示例问题。这只是因为你有一张额外的表格而更复杂,因此需要考虑更多的场景。

在我看来,您可以向下减少这四种情况:

  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语句都引用该组合,则不会执行这些组合。

+0

提交的代码有它一些微小的问题,但核心思想帮了不少​​忙。更正后的代码:http://pastebin.com/QBiyRrnR(我用更多的连接替换了子查询,因为连接通常在性能上更好) – David162795