为什么这个简单的Left Join从不匹配的行返回数据?

时间:2018-03-23 12:05:09

标签: mysql sql left-join

请注意操作中此问题的简单http://sqlfiddle.com/#!9/e853f/1

我指的是MySQL ver 5.6.12-log

根据我的理解,左连接为最右边数据集中的列返回NULL,其中左侧数据集中的键不存在于右侧数据集中。

但是,即使右手边没有左手键,我也会从右侧返回数据。

有谁能解释这里发生了什么?

SQLfiddle创建:

  • 一个包含6行的表,每行只包含一个整数ID
  • 包含3行的第二个表,其中包含一些整数ID加上两个 更多INT字段
  • 基于第二个表的视图,该表返回包含整数ID的3行以及从其他两个INT字段派生的文本字段

(显然,视图中的3个ID对应于6行表中的某些ID。)

SQL SELECT * FROM LEFT JOIN on table_ID = view_ID; 按预期返回6行,但所有在文本字段中有数据,而不是3个不匹配的数据为NULL

BUT

如果视图中用于派生文本列的方法稍有改动,那么Left Join SQL会给出正确的结果。 (您可以通过选择性地在sql小提琴中注释掉这两种方法中的一种或其他方式来显示这一点)

但是,优化者肯定不会首先评估视图,因此创建数据的方式并不重要,只是它包含的内容是什么?

(这是我早先提出的一个问题的简化版本,我承认这个版本太复杂了,不合理的答案)

有人建议(Jeroen Mostert)我会显示数据和预期结果。这是:

表人

personID
--------
   1
   2
   3
   4
   5
   6

查看payment_state

payment_personID  |   state
----------------------------
       1          |   'equal'
       2          |   'under'
       3          |   'over'

查询

SELECT * FROM  person 
LEFT JOIN   payment_state 
ON personID = payment_personID;

预期结果

personID | payment_personID  |state
-------------------------------------
    1    |      1            | 'equal'
    2    |      2            | 'under'
    3    |      3            | 'over'
    4    |     NULL          |  NULL
    5    |     NULL          |  NULL
    6    |     NULL          |  NULL

实际结果

personID | payment_personID  |state
-------------------------------------
    1    |      1            | 'equal'
    2    |      2            | 'under'
    3    |      3            | 'over'
    4    |     NULL          | 'equal'
    5    |     NULL          | 'equal'
    6    |     NULL          | 'equal'

3 个答案:

答案 0 :(得分:2)

我不同意其他答案。 这是一个MySQL缺陷。实际上它是MySQL 5.6中的bug #83707。它看起来已经在MySQL 5.7中修复了

此错误已在MariaDB 5.5中修复。

内部连接策略(如嵌套循环连接,合并连接或散列连接)无关紧要。无论如何,结果应该是正确的。

我在PostgreSQL和Oracle中尝试了相同的查询,它按预期工作,在最后三行返回空值。

Oracle示例

CREATE TABLE person (personID INT); 

INSERT INTO person (personID) VALUES (1); 
INSERT INTO person (personID) VALUES(2); 
INSERT INTO person (personID) VALUES(3);
INSERT INTO person (personID) VALUES(4);
INSERT INTO person (personID) VALUES(5);
INSERT INTO person (personID) VALUES(6);

CREATE TABLE payments (
   payment_personID INT,
   Due INT,
   Paid INT) ;

INSERT INTO payments  (payment_personID, due, paid) VALUES (1, 5, 5);
INSERT INTO payments  (payment_personID, due, paid) VALUES (2, 5, 3);
INSERT INTO payments  (payment_personID, due, paid) VALUES (3, 5, 8);

CREATE VIEW payment_state AS (
SELECT
   payment_personID,
  CASE 
   WHEN COALESCE(paid,0) < COALESCE(due,0) AND due <> 0 THEN 'under' 
   WHEN COALESCE(paid,0) > COALESCE(due,0) THEN 'over' 
   WHEN COALESCE(paid,0) = COALESCE(due,0) THEN 'equal' 
   END AS state 
FROM payments);

SELECT *
FROM
    person
LEFT JOIN 
    payment_state   
ON personID = payment_personID;

结果:

PERSONID  PAYMENT_PERSONID  STATE
========  ================  =====
       1                 1  equal
       2                 2  under
       3                 3  over
       6            <null>  <null>
       5            <null>  <null>
       4            <null>  <null>

完美无缺!

PostgreSQL示例

CREATE TABLE person (personID INT); 
INSERT INTO person (personID) VALUES
(1),(2),(3),(4),(5),(6);

CREATE TABLE payments (
   payment_personID INT,
   Due INT,
   Paid INT) ;

INSERT INTO payments  (payment_personID, due, paid) VALUES
(1, 5, 5), (2, 5, 3), (3, 5, 8);

CREATE VIEW payment_state AS (
SELECT
   payment_personID,
  CASE 
   WHEN COALESCE(paid,0) < COALESCE(due,0) AND due <> 0 THEN 'under' 
   WHEN COALESCE(paid,0) > COALESCE(due,0) THEN 'over' 
   WHEN COALESCE(paid,0) = COALESCE(due,0) THEN 'equal' 
   END AS state 
FROM payments);

SELECT *
FROM
    person
LEFT JOIN 
    payment_state   
ON personID = payment_personID;

结果:

personid  payment_personid  state
========  ================  =====
       1                 1  equal
       2                 2  under
       3                 3  over
       4            <null>  <null>
       5            <null>  <null>
       6            <null>  <null>

另外,效果很好!

答案 1 :(得分:1)

视图的处理算法会导致此结果。默认情况下,MySQL通常会选择MERGE,因为它更有效。如果使用“TEMPTABLE”算法创建视图,则可以看到不匹配行的NULL。

http://www.mysqltutorial.org/create-sql-views-mysql.aspx

CREATE ALGORITHM =  TEMPTABLE VIEW  payment_state AS (
SELECT
   payment_personID,
 CASE 
   WHEN IFNULL(paid,0) < IFNULL(due,0) AND due <> 0 THEN 'under' 
   WHEN IFNULL(paid,0) > IFNULL(due,0) THEN 'over' 
   WHEN IFNULL(paid,0) = IFNULL(due,0) THEN 'equal' 
   END AS state 
FROM payments);

答案 2 :(得分:0)

这是LEFT JOIN正常工作的方式。它会在结果中附加新列,然后用以下内容填充:

    如果JOIN成功,则
  • JOIN表中提取的值
  • 如果NULL不匹配(包括您加入的字段JOIN),则
  • ON

通常情况下,NULL从真实表格(其中JOIN匹配)和NULL填充的JOIN之间没有区别,因为CASE不匹配。 IFNULL + NULL只需查找0并将其转换为NULL s(无论其来源)。这就是为什么即使在不匹配的行中也会在 state 列中生成结果。

事实上,如果您想知道您所查看的某个JOIN是否与JOIN不匹配,您需要明确检查这一点 - 如果所有关键字段都是如此如果此列中的NULL是填写的结果,则NULL的{​​{1}}为NULL。如果此行中存在来自键的字段,但其他列中仍有JOIN,那么它就在那里,因为它是从您SELECT DISTINCT COUNT(*) OVER () AS TotalRecords FROM table GROUP BY column 编辑的表格中提取的。