如何在MySQL中进行全面的连接?

时间:2011-01-25 17:34:19

标签: sql mysql join outer-join full-outer-join

我想在MySQL中进行全外连接。这可能吗? MySQL是否支持Full Outer Join?

15 个答案:

答案 0 :(得分:558)

您在MySQL上没有完全加入,但您可以确定emulate them

对于从this SO question转录的代码SAMPLE,您有:

有两个表t1,t2:

SELECT * FROM t1
LEFT JOIN t2 ON t1.id = t2.id
UNION
SELECT * FROM t1
RIGHT JOIN t2 ON t1.id = t2.id

上面的查询适用于FULL OUTER JOIN操作不会产生任何重复行的特殊情况。上面的查询取决于UNION set运算符,以删除查询模式引入的重复行。我们可以通过对第二个查询使用反连接模式来避免引入重复行,然后使用UNION ALL集合运算符来组合这两个集合。在更一般的情况下,FULL OUTER JOIN将返回重复的行,我们可以这样做:

SELECT * FROM t1
LEFT JOIN t2 ON t1.id = t2.id
UNION ALL
SELECT * FROM t1
RIGHT JOIN t2 ON t1.id = t2.id
WHERE t1.id IS NULL

答案 1 :(得分:324)

Pablo Santa Cruz给出的答案是正确的;但是,如果有人偶然发现了这个页面并想要进一步澄清,这里有一个详细的细分。

示例表

假设我们有以下表格:

-- t1
id  name
1   Tim
2   Marta

-- t2
id  name
1   Tim
3   Katarina

内部联接

内部联接,如下所示:

SELECT *
FROM `t1`
INNER JOIN `t2` ON `t1`.`id` = `t2`.`id`;

只会让我们看到两个表中出现的记录,如下所示:

1 Tim  1 Tim

内连接没有方向(如左或右),因为它们是明确双向的 - 我们需要在两侧都匹配。

外部联接

另一方面,外连接用于查找在另一个表中可能没有匹配的记录。因此,您必须指定允许连接的哪一方具有丢失的记录。

LEFT JOINRIGHT JOINLEFT OUTER JOINRIGHT OUTER JOIN的简写;我将使用下面的全名来强化外连接与内连接的概念。

左外连接

左外连接,如下所示:

SELECT *
FROM `t1`
LEFT OUTER JOIN `t2` ON `t1`.`id` = `t2`.`id`;

...无论是否在右表中匹配,我们都会从左表中获取所有记录,如下所示:

1 Tim   1    Tim
2 Marta NULL NULL

正确的外部加入

右外连接,如下所示:

SELECT *
FROM `t1`
RIGHT OUTER JOIN `t2` ON `t1`.`id` = `t2`.`id`;

...无论左表中是否匹配,都会从右表中获取所有记录,如下所示:

1    Tim   1  Tim
NULL NULL  3  Katarina

完全外部加入

完全外部联接将为我们提供来自两个表的所有记录,无论它们是否在另一个表中匹配,在没有匹配的情况下两侧都有NULL。结果如下:

1    Tim   1    Tim
2    Marta NULL NULL
NULL NULL  3    Katarina

然而,正如Pablo Santa Cruz指出的那样,MySQL并不支持这一点。我们可以通过执行左连接和右连接的UNION来模拟它,如下所示:

SELECT *
FROM `t1`
LEFT OUTER JOIN `t2` ON `t1`.`id` = `t2`.`id`

UNION

SELECT *
FROM `t1`
RIGHT OUTER JOIN `t2` ON `t1`.`id` = `t2`.`id`;

您可以将UNION视为“运行这两个查询,然后将结果叠加在一起”;一些行将来自第一个查询,一些来自第二个查询。

应该注意MySQL中的UNION将消除完全重复:Tim会出现在这两个查询中,但UNION的结果只列出他一次。我的数据库大师同事觉得不应该依赖这种行为。因此,为了更加明确,我们可以在第二个查询中添加WHERE子句:

SELECT *
FROM `t1`
LEFT OUTER JOIN `t2` ON `t1`.`id` = `t2`.`id`

UNION

SELECT *
FROM `t1`
RIGHT OUTER JOIN `t2` ON `t1`.`id` = `t2`.`id`
WHERE `t1`.`id` IS NULL;

另一方面,如果您想要出于某种原因查看重复项,则可以使用UNION ALL

答案 2 :(得分:30)

使用union查询将删除重复项,这与full outer join的行为不同,后者永远不会删除任何重复:

[Table: t1]                            [Table: t2]
value                                  value
-------                                -------
1                                      1
2                                      2
4                                      2
4                                      5

这是full outer join

的预期结果
value | value
------+-------
1     | 1
2     | 2
2     | 2
Null  | 5
4     | Null
4     | Null

这是将leftright Joinunion一起使用的结果:

value | value
------+-------
Null  | 5 
1     | 1
2     | 2
4     | Null

[SQL Fiddle]

我建议的查询是:

select 
    t1.value, t2.value
from t1 
left outer join t2  
  on t1.value = t2.value
union all      -- Using `union all` instead of `union`
select 
    t1.value, t2.value
from t2 
left outer join t1 
  on t1.value = t2.value
where 
    t1.value IS NULL 

上述查询的结果与预期结果相同:

value | value
------+-------
1     | 1
2     | 2
2     | 2
4     | NULL
4     | NULL
NULL  | 5

[SQL Fiddle]

  

@Steve Chambers [来自评论,非常感谢!]
  注意:这可能是最佳解决方案,既可以提高效率,也可以生成与FULL OUTER JOIN相同的结果。 This blog post也很好地解释了 - 引用方法2:"它正确​​处理重复的行,不包含任何不应包含的行。有必要使用UNION ALL而不是普通UNION,这将消除我想要保留的重复项。对于大型结果集,这可能会显着提高效率,因为不需要对重复项进行排序和删除。"

我决定添加另一个来自full outer join可视化和数学的解决方案,它不是更好,但更可读:

  

全外联接意味着(t1 ∪ t2):全部在t1t2中   (t1 ∪ t2) = (t1 ∩ t2) + t1_only + t2_onlyt1t2中的所有内容以及t1t2以及t2中的所有t1中的所有内容不在-- (t1 ∩ t2): all in both t1 and t2 select t1.value, t2.value from t1 join t2 on t1.value = t2.value union all -- And plus -- all in t1 that not exists in t2 select t1.value, null from t1 where not exists( select 1 from t2 where t2.value = t1.value) union all -- and plus -- all in t2 that not exists in t1 select null, t2.value from t2 where not exists( select 1 from t1 where t2.value = t1.value)

enum Shade
{
    White = 0, 
    Gray = 1, 
    Grey = 1, 
    Black = 2 
}

[SQL Fiddle]

答案 3 :(得分:4)

在SQLite中你应该这样做:

SELECT * 
FROM leftTable lt 
LEFT JOIN rightTable rt ON lt.id = rt.lrid 
UNION
SELECT lt.*, rl.*  -- To match column set
FROM rightTable rt 
LEFT JOIN  leftTable lt ON lt.id = rt.lrid

答案 4 :(得分:4)

上述答案都不是正确的,因为当存在重复值时,它们不遵循语义。

对于诸如(来自此duplicate)的查询:

SELECT * FROM t1 FULL OUTER JOIN t2 ON t1.Name = t2.Name;

正确的等价物是:

SELECT t1.*, t2.*
FROM (SELECT name FROM t1 UNION  -- This is intentionally UNION to remove duplicates
      SELECT name FROM t2
     ) n LEFT JOIN
     t1
     ON t1.name = n.name LEFT JOIN
     t2
     ON t2.name = n.name;

如果您需要使用NULL值(可能还需要),请使用NULL - 安全比较运算符<=>而不是=

答案 5 :(得分:4)

MySql没有FULL-OUTER-JOIN语法。您必须通过执行LEFT JOIN和RIGHT JOIN来模拟,如下所示 -

SELECT * FROM t1
LEFT JOIN t2 ON t1.id = t2.id  
UNION
SELECT * FROM t1
RIGHT JOIN t2 ON t1.id = t2.id

但MySql也没有RIGHT JOIN语法。根据MySql的outer join simplification,通过切换查询中FROMON子句中的t1和t2,将右连接转换为等效的左连接。因此,MySql查询优化器将原始查询转换为以下内容 -

SELECT * FROM t1
LEFT JOIN t2 ON t1.id = t2.id  
UNION
SELECT * FROM t2
LEFT JOIN t1 ON t2.id = t1.id

现在,按原样编写原始查询没有什么害处,但是如果你有像WHERE子句这样的谓词,它是before-join谓词或ON子句的AND谓词,这是during-join谓词,那么你可能想看看魔鬼;这是详细的。

MySql查询优化器会定期检查谓词是否 null-rejected Null-Rejected Definition and Examples 现在,如果您已经完成了RIGHT JOIN,但是在t1的列上使用了WHERE谓词,那么您可能会遇到运行 null-rejected 方案的风险。

例如, 以下查询 -

SELECT * FROM t1
LEFT JOIN t2 ON t1.id = t2.id
WHERE t1.col1 = 'someValue'
UNION
SELECT * FROM t1
RIGHT JOIN t2 ON t1.id = t2.id
WHERE t1.col1 = 'someValue'

由查询优化程序转换为以下内容 -

SELECT * FROM t1
LEFT JOIN t2 ON t1.id = t2.id
WHERE t1.col1 = 'someValue'
UNION
SELECT * FROM t2
LEFT JOIN t1 ON t2.id = t1.id
WHERE t1.col1 = 'someValue'

因此表的顺序已更改,但谓词仍然应用于t1,但t1现在位于“ON”子句中。如果t1.col1定义为NOT NULL 列,然后此查询将 null-rejected

null-rejected 的任何外连接(左,右,整)都由MySql转换为内连接。

因此,您可能期望的结果可能与MySql返回的结果完全不同。您可能认为它是MySql的RIGHT JOIN的一个错误,但那不对。它就是MySql查询优化器的工作原理。因此,负责开发者在构建查询时必须注意这些细微差别。

答案 6 :(得分:3)

修改了shA.t的查询以获得更清晰:

-- t1 left join t2
SELECT t1.value, t2.value
FROM t1 LEFT JOIN t2 ON t1.value = t2.value   

    UNION ALL -- include duplicates

-- t1 right exclude join t2 (records found only in t2)
SELECT t1.value, t2.value
FROM t1 RIGHT JOIN t2 ON t1.value = t2.value
WHERE t2.value IS NULL 

答案 7 :(得分:2)

您可以执行以下操作:

- (void)setPlayDefaultSound:(BOOL)playDefaultSound
{

   [[NSUserDefaults standardUserDefaults] setBool:playDefaultSound forKey:@"playDefaultSound"];
   [[NSUserDefaults standardUserDefaults] synchronize];
}
- (BOOL)playDefaultSound
{
    return [[NSUserDefaults standardUserDefaults] boolForKey:@"playDefaultSound"];
}

答案 8 :(得分:1)

您可以只转换完整的外部联接,例如

SELECT fields
FROM firsttable
FULL OUTER JOIN secondtable ON joincondition

进入:

SELECT fields
FROM firsttable
LEFT JOIN secondtable ON joincondition
UNION ALL
SELECT fields (replacing any fields from firsttable with NULL)
FROM secondtable
WHERE NOT EXISTS (SELECT 1 FROM firsttable WHERE joincondition)

或者如果您在foo中至少有一个列,例如firsttable,该列不是NULL,则可以执行以下操作:

SELECT fields
FROM firsttable
LEFT JOIN secondtable ON joincondition
UNION ALL
SELECT fields
FROM firsttable
RIGHT JOIN secondtable ON joincondition
WHERE firsttable.foo IS NULL

答案 9 :(得分:0)

SELECT
    a.name,
    b.title
FROM
    author AS a
LEFT JOIN
    book AS b
    ON a.id = b.author_id
UNION
SELECT
    a.name,
    b.title
FROM
    author AS a
RIGHT JOIN
    book AS b
    ON a.id = b.author_id

答案 10 :(得分:0)

您对Cross join解决方案的评价是什么?

SELECT t1.*, t2.*
FROM table1 t1
INNER JOIN table2 t2 
ON 1=1;

答案 11 :(得分:-1)

这也是可能的,但您必须在select中提及相同的字段名称。

SELECT t1.name, t2.name FROM t1
LEFT JOIN t2 ON t1.id = t2.id
UNION
SELECT t1.name, t2.name FROM t2
LEFT JOIN t1 ON t1.id = t2.id

答案 12 :(得分:-1)

我修复了响应,并且包括所有行(基于Pavle Lekic的响应)

    (
    SELECT a.* FROM tablea a
    LEFT JOIN tableb b ON a.`key` = b.key
    WHERE b.`key` is null
    )
    UNION ALL
    (
    SELECT a.* FROM tablea a
    LEFT JOIN tableb b ON a.`key` = b.key
    where  a.`key` = b.`key`
    )
    UNION ALL
    (
    SELECT b.* FROM tablea a
    right JOIN tableb b ON b.`key` = a.key
    WHERE a.`key` is null
    );

答案 13 :(得分:-2)

<强>答案:

SELECT * FROM t1 FULL OUTER JOIN t2 ON t1.id = t2.id;

可以重新创建如下:

 SELECT t1.*, t2.* 
 FROM (SELECT * FROM t1 UNION SELECT name FROM t2) tmp
 LEFT JOIN t1 ON t1.id = tmp.id
 LEFT JOIN t2 ON t2.id = tmp.id;

使用UNION或UNION ALL答案不包括基表具有重复条目的边缘情况。

<强>解释

有一个UNION或UNION ALL无法覆盖的边缘情况。我们无法在mysql上测试它,因为它不支持FULL OUTER JOIN,但是我们可以在支持它的数据库上说明这一点:

 WITH cte_t1 AS
 (
       SELECT 1 AS id1
       UNION ALL SELECT 2
       UNION ALL SELECT 5
       UNION ALL SELECT 6
       UNION ALL SELECT 6
 ),
cte_t2 AS
(
      SELECT 3 AS id2
      UNION ALL SELECT 4
      UNION ALL SELECT 5
      UNION ALL SELECT 6
      UNION ALL SELECT 6
)
SELECT  *  FROM  cte_t1 t1 FULL OUTER JOIN cte_t2 t2 ON t1.id1 = t2.id2;

This gives us this answer:

id1  id2
1  NULL
2  NULL
NULL  3
NULL  4
5  5
6  6
6  6
6  6
6  6

UNION解决方案:

SELECT  * FROM  cte_t1 t1 LEFT OUTER JOIN cte_t2 t2 ON t1.id1 = t2.id2
UNION    
SELECT  * FROM cte_t1 t1 RIGHT OUTER JOIN cte_t2 t2 ON t1.id1 = t2.id2

给出错误答案:

 id1  id2
NULL  3
NULL  4
1  NULL
2  NULL
5  5
6  6

UNION ALL解决方案:

SELECT  * FROM cte_t1 t1 LEFT OUTER join cte_t2 t2 ON t1.id1 = t2.id2
UNION ALL
SELECT  * FROM  cte_t1 t1 RIGHT OUTER JOIN cte_t2 t2 ON t1.id1 = t2.id2

也不正确。

id1  id2
1  NULL
2  NULL
5  5
6  6
6  6
6  6
6  6
NULL  3
NULL  4
5  5
6  6
6  6
6  6
6  6

这个查询:

SELECT t1.*, t2.*
FROM (SELECT * FROM t1 UNION SELECT name FROM t2) tmp 
LEFT JOIN t1 ON t1.id = tmp.id 
LEFT JOIN t2 ON t2.id = tmp.id;

给出以下内容:

id1  id2
1  NULL
2  NULL
NULL  3
NULL  4
5  5
6  6
6  6
6  6
6  6

订单不同,但在其他方面与正确的答案相符。

答案 14 :(得分:-2)

SQL标准说$query = "SELECT * FROM `devicehotsheet`"; $options = ""; $result1 = mysql_query($query, $dbhandle); while ($row1= mysql_fetch_array($result1)){ $options=$options."<option>$row1[0]</option>"; } <select class="selectDevice" id="selectDevice" onChange="calculateTotal()"> <?php echo $options; ?> </select> <select class="myport" id="myport" onChange="calculateTotal()"> <option class="price" value=" ">Price</option> </select> import android.content.Intent; import android.preference.PreferenceActivity; import android.os.Bundle; import android.preference.PreferenceFragment; public class SettingsActivity extends PreferenceActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getFragmentManager().beginTransaction().replace(android.R.id.content, new MainSettingsFragment()).commit(); } public static class MainSettingsFragment extends PreferenceFragment { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); addPreferencesFromResource(R.xml.settings); } } @Override public void onBackPressed() { super.onBackPressed(); this.finish(); Intent intent = new Intent(this, MainActivity.class); startActivity(intent); } } 不匹配的左表行,扩展为空full join on右表行,扩展为空。即inner join on中的union allunion all行,而不是inner join on中的union all left join on行,而不是inner join on中的行。

union allright join on inner join on行不在left join on中。或者,如果您知道您的union all结果在特定的右表列中不能为null,则“ right join on中不存在的inner join on行是inner join on中具有{{ 1}}条件由right join on扩展了列inner join on

类似地,right join on on适当的and行。

来自What is the difference between “INNER JOIN” and “OUTER JOIN”?

  

(SQL标准2006 SQL / Foundation 7.7语法规则1,一般规则1 b,3 c和d,5 b。)