MYSQL FULL OUTER JOIN - 使用 LEFT-UNION-LEFT JOIN 时的所有 NULL 结果

Posted

技术标签:

【中文标题】MYSQL FULL OUTER JOIN - 使用 LEFT-UNION-LEFT JOIN 时的所有 NULL 结果【英文标题】:MYSQL FULL OUTER JOIN - All NULL results when using LEFT-UNION-LEFT JOIN 【发布时间】:2018-02-28 14:11:13 【问题描述】:

Tbl_A

cap_id| yr_a| sp_a| iso_a| area_a| qty_a | prod_a |
     3| 2015|  TRR|    54|      8|   120 |      0 |
   678| 2015|  BOM|    62|     27|   0.0 |      0 |
    20| 2015|  TRR|    54|     27|   0.0 |      0 |
    45| 2015|  FRC|     7|     15| 86800 |      0 |
    52| 2015|  AZB|    12|      6|   987 |      0 |

Tbl_B

aqua_id| yr_b| sp_b| iso_b| area_b| qty_b | prod_b |
     78| 2015|  OTS|    32|     27|  6868 |      1 |   
    333| 2015|  FRC|     7|     15|   550 |      1 |
    334| 2015|  FRC|     7|     15|   550 |      2 |      
    789| 2015|  TRR|    54|     27| 45000 |      3 |
    987| 2015|  TRR|    32|     27|    40 |      2 |

我得到了我正在寻找的 FULL OUTER JOIN

但是查询也生成了一大堆所有 NULL 记录(ID 在 Tbl_C 中的 7-9)

Tbl_C - 最终 Tbl

id| cap_id| aqua_id| yr_a| yr_b| sp_a| sp_b| iso_a| iso_b|area_a|area_b| qty_a| qty_b | prod_a | prod_b
1 |     20|     789| 2015| 2015|  TRR|  TRR|    54|    54|    27|    27|   0.0| 45000 | 0      | 1
2 |     45|     333| 2015| 2015|  FRC|  FRC|     7|     7|    15|    15| 86800| 550   | 0      | 1
3 |     45|     334| 2015| 2015|  FRC|  FRC|     7|     7|    15|    15| 86800| 550   | 0      | 2
4 |    678|    NULL| 2015| NULL|  BOM| NULL|    62|  NULL|    27|  NULL|   0.0| NULL  | 0      | NULL
5 |      3|    NULL| 2015| NULL|  TRR| NULL|    54|  NULL|     8|  NULL|   120| NULL  | 0      | NULL
6 |   NULL|      78| NULL| 2015| NULL|  OTS|  NULL|    32|  NULL|    27|  NULL| 6868  | 0      | 1
7 |   NULL|     987| NULL| 2015| NULL|  TRR|  NULL|    32|  NULL|    27|  NULL| 40    | 0      | 2
8 |   NULL|    NULL| NULL| NULL| NULL| NULL|  NULL|  NULL|  NULL|  NULL|  NULL| NULL  | NULL   | NULL
9 |   NULL|    NULL| NULL| NULL| NULL| NULL|  NULL|  NULL|  NULL|  NULL|  NULL| NULL  | NULL   | NULL

我正在尝试找出导致额外多条全 NULL 记录的原因?

使用的查询是:

(SELECT a.cap_id, b.aqua_id, a.yr_a, b.yr_b,...., a.qty_a, b.qty_b
FROM Tbl A AS a LEFT JOIN Tbl_B AS b
ON a.yr_a = b.yr_b 
AND a.iso_a = b.iso_b
AND a.area_a = b.area_b
AND a.sp_a = b.sp_b
WHERE a.yr_a = 2015)
UNION
(SELECT a.cap_id, b.aqua_id, a.yr_a, b.yr_b,...., a.qty_a, b.qty_a
FROM  Tbl_B AS b LEFT JOIN Tble_A AS a
ON a.yr_a = b.yr_b
AND a.iso_a = b.iso_b
AND a.area_a = b.area_b
AND a.sp_a = b.sp_b
WHERE b.yr_b = 2015);

【问题讨论】:

我猜你有 iso_a、yr_a、area_a 和 sp_a 都为空的记录。 select * from tbl_A where iso_a is null and yr_a is null and area_a is null and sp_a is null 会产生结果吗?除了 tbl_b,同样的事情呢?为什么颠倒连接的顺序只是让它成为一个正确的连接并且一切都保持不变? 你有这4个字段为空的记录吗? 【参考方案1】:

演示:http://rextester.com/AWDYA21027 使用您的示例数据和查询我没有得到重复的空列。这意味着数据问题。

为什么要让它成为左连接?只需从左到右切换,并将 where 子句切换到 b.yr_a。

为了消除空值,通过消除所有 4 个值都为空的记录来确保至少有一个连接条件匹配?

给定您的示例数据,将返回完整的外连接:当按 cap_Id 和 aqua_ID 排序时

+--------+---------+------+------+-------+-------+
| CAP_ID | AQUA_ID | YR_A | YR_B | QTY_A | QTY_B |
+--------+---------+------+------+-------+-------+
|        |      78 |      | 2015 |       |  6868 |
|        |     987 |      | 2015 |       |    40 |
|      3 |         | 2015 |      |   120 |       |
|     20 |     789 | 2015 | 2015 |     0 | 45000 |
|     45 |     333 | 2015 | 2015 | 86800 |   550 |
|    678 |         | 2015 |      |     0 |       |
+--------+---------+------+------+-------+-------+

以下查询返回:

+----+--------+---------+------+------+----------+----------+
|    | cap_id | aqua_id | yr_a | yr_b |  qty_a   |  qty_b   |
+----+--------+---------+------+------+----------+----------+
|  1 | NULL   | 78      | NULL | 2015 | NULL     | 6868,00  |
|  2 | NULL   | 987     | NULL | 2015 | NULL     | 40,00    |
|  3 | 3      | NULL    | 2015 | NULL | 120,00   | NULL     |
|  4 | 20     | 789     | 2015 | 2015 | 0,00     | 45000,00 |
|  5 | 45     | 333     | 2015 | 2015 | 86800,00 | 550,00   |
|  6 | 678    | NULL    | 2015 | NULL | 0,00     | NULL     |
+----+--------+---------+------+------+----------+----------+

这似乎是完全外连接的正确结果。 如果我在 A 或 B 中有重复值,则此查询将返回这些重复值,因为 UNION 中的不同值不会发生,因为我们正在执行全部联合;不是工会。

(SELECT a.cap_id, b.aqua_id, a.yr_a, b.yr_b, a.qty_a, b.qty_b
FROM tbl_A AS a 
LEFT JOIN tbl_B AS b
  ON a.yr_a = b.yr_b 
 AND a.iso_a = b.iso_b
 AND a.area_a = b.area_b
 AND a.sp_a = b.sp_b
WHERE a.yr_a = 2015
  and (a.yr_a is not null 
   or a.iso_a is not null 
   or a.area_a is not null 
   or a.sp_a is not null))
UNION ALL
(SELECT a.cap_id, b.aqua_id, a.yr_a, b.yr_b, a.qty_a, b.qty_b
FROM tbl_A AS a 
RIGHT JOIN tbl_B AS b
   ON a.yr_a = b.yr_b 
  AND a.iso_a = b.iso_b
  AND a.area_a = b.area_b
  AND a.sp_a = b.sp_b
WHERE b.yr_b = 2015
  and (b.yr_b is not null 
   or b.iso_b is not null 
   or b.area_b is not null 
   or b.sp_b is not null)
 and a.iso_a is null #to exclude extra nulls duplicated by union all.
 );

【讨论】:

tks! LEFT-UNION_RIGHT 没有给我我需要的 FULL OUTER JOIN 连接。它删除了重复项,例如 cap_id、年份、物种、iso 都匹配但不止一种生产类型(样本表是从更大的列集中简化的)。为了解决这个问题,我在这里建议使用 Left-Union-Left:(@Steve Chambers ***.com/questions/4796872/…)。 @possbil 那么您是否有 iso_a 为空且 yr_a 为空且 area_a 为空且 sp_a 为空的记录?无论哪种方式,我在演示中都得到了相同的结果;所以我必须在你的表中丢失数据。 @possbil 链接说使用联合所有和左/右连接。不离开/离开。 xaprb.com/blog/2006/05/26/how-to-write-full-outer-join-in-mysql 并且您需要在第二次加入时排除 a.key 为空的情况,以消除联合上的多余重复项。 链接下方对 LEFT-UNION-LEFT 的解释。我已经修改了示例表以尝试显示场景。 LEFT-UNION-RIGHT 删除 Tbl_C 中的 id 3。对于 ON 子句中使用的列,Tbl_a 和 Tbl_B 没有任何 NULL。确实发生在 Tbl_C 结果中(显然:)) 可能是数据 Tble_A = 900,00 行 Tbl_B = 86,000 行。但怀疑错误是人为的:)【参考方案2】:

答案似乎很简单。 如果您选择第一个并运行它:

SELECT a.*, b.*
FROM Tbl_A AS a LEFT JOIN Tbl_B AS b
ON a.yr_a = b.yr_b 
AND a.iso_a = b.iso_b
AND a.area_a = b.area_b
AND a.sp_a = b.sp_b
WHERE a.yr_a = 2015

您可能应该得到 5 行结果,因为 5 行是 Tbl_A。问题在于,由于连接标准,并非所有这些行都与 Tbl_B 中的行连接。因此,当您连接这些表时,对于 Tbl_A 的某些行,Tbl_B 的某些列将是 NULL,因为连接是 Outer。

这同样适用于您的第二个查询:

SELECT a.*, b.*
FROM  Tbl_B AS b LEFT JOIN Tbl_A AS a
ON a.yr_a = b.yr_b
AND a.iso_a = b.iso_b
AND a.area_a = b.area_b
AND a.sp_a = b.sp_b
WHERE b.yr_b = 2015

Tbl_B 中的行与 Tbl_A 没有精确连接,因此,由于您离开连接到 B,因此 Tbl_B 中的行将包含大量 NULL。

我建议您逐个运行它们并查看每个查询的结果。

【讨论】:

感谢 ekalyvio!。是的,只是学习 MySQL。在同一个 WHERE 子句中,我无法理解 IS NULL is NOT NULL。一分钱终于掉了。从自己的大脑屁中学习总是一件好事:)。【参考方案3】:

NULLS 的产生是因为: 如果第二个 LEFT JOIN 中的 ON 子句变量之一的 WHERE 子句中没有“IS NULL”,则 INNER JOIN 会运行两次! 由于我被留下将较短的表连接到较长的表(对于 Tbl_B

【讨论】:

以上是关于MYSQL FULL OUTER JOIN - 使用 LEFT-UNION-LEFT JOIN 时的所有 NULL 结果的主要内容,如果未能解决你的问题,请参考以下文章

如何在 MySQL 中进行 FULL OUTER JOIN?

如何在 MySQL 中进行 FULL OUTER JOIN?

如何在 MySQL 中进行 FULL OUTER JOIN?

MySQL中没有FULL OUTER JOIN的处理

MySQL:FULL OUTER JOIN - 如何合并一列?

在 MySQL 中执行 FULL OUTER JOIN 查询时出错 [重复]