MySQL:简介 Ubuntu 安装 MySQL MySQL:官方文档地址 MySQL:DDL、DQL、DML、DCL的含义 MySQL:Mac 中启动 关闭 MySQL 服务 MySQL:有哪些好用的管理工具? MySQL:命令行工具 mycli MySQL:CHAR类型 MySQL:VARCHAR类型 MySQL:整型数字 MySQL:datetime 类型 MySQL:时间戳 MySQL:创建和删除数据库 MySQL:切换和查看数据库 MySQL:创建和删除表 MySQL:在表中增加、删除、修改列 MySQL:创建和删除主键 MySQL:使用 rename 修改表名 MySQL:修改自增主键id的类型 MySQL:如何创建一个相同的表 MySQL:修改表的字符编码 MySQL:增删查改 MySQL:插入数据 MySQL:插入多行数据 MySQL:使用 insert set 插入数据 MySQL:大小写和反引号 MySQL:字符串类型值的大小写 MySQL:SQL注释 MySQL:不要使用utf8 MySQL:NULL的判等 MySQL:InnoDB存储引擎的限制 MySQL:if和case的使用 MySQL:使用 load data 快速导入数据 MySQL:使用 select into outfile 导出数据 MySQL:查询和设置 sql_mode MySQL:严格模式 MySQL:NOT NULL 字段不插入数据,会发生什么? MySQL:无符号整数列插入负数会发生什么? MySQL:关于 null 的那些事 MySQL:大表行数查询 MySQL:自动生成创建时间、更新时间;自动更新更新时间 MySQL:insert ignore MySQL:字符集排序规则 MySQL:如果连续更新一个字段两次,结果是? MySQL:字符串转数字 MySQL:尾部空格 MySQL:添加和删除索引 MySQL:唯一索引与NULL MySQL:唯一索引的单列长度限制 MySQL:InnoDB 索引 MySQL:字符集排序规则对唯一索引的影响 MySQL:唯一索引冲突消耗主键 ID MySQL 使用 index hint 指定索引:ignore index、force index、use index MySQL:查看客户端连接信息 MySQL:查看表的状态 show table status MySQL:如何治理连接数 ? MySQL:如何监控和处理慢查询与长事务 ? MySQL:自定义函数 MySQL:now() 函数 MySQL:unix_timestamp() 函数 MySQL:from_unixtime() 函数 MySQL:version() 函数 MySQL:current_timestamp() 函数 MySQL:cast 函数 MySQL:convert 函数 MySQL:使用 greatest、least 函数获取行最大值、最小值 MySQL:使用 group_concat 函数连接多行数据为一个字符串 MySQL:获取版本号 MySQL:Java 类型映射 MySQL下创建只能有一行记录的table 关于MySQL的字符集 理解数据库中的undo日志、redo日志、检查点 ubuntu下源码安装MySQL MySQL:JOIN解惑 如何快速更新数据库中的百万条数据

MySQL:JOIN解惑


#MySQL


2014-07-31

What's the difference between INNER JOIN, LEFT JOIN, RIGHT JOIN and FULL JOIN? 给出了一个解释各种JOIN的图:

下面用示例解释一下它们在MySQL中的区别。

创建两个表

两个表分别命名为users和blogs。users表用来存储用户信息;blogs表用来存储用户写的博客,为了简化,列只有user_idtitle,没有主键,content类似的字段也没有。

CREATE TABLE `users` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` char(1) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8;

CREATE TABLE `blogs` (
  `user_id` int(11) NOT NULL,
  `title` char(1) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8

添加数据:

mysql> SELECT * FROM users;
+----+------+
| id | name |
+----+------+
|  1 | w    |
|  2 | x    |
|  3 | y    |
|  4 | z    |
+----+------+

mysql> SELECT * FROM blogs;
+---------+-------+
| user_id | title |
+---------+-------+
|       1 | r     |
|       2 | s     |
|       3 | t     |
|       1 | q     |
|      10 | p     |
+---------+-------+

CROSS JOIN

CROSS JOIN就是对两个表求笛卡尔积(Cartesian product):

mysql> SELECT users.id, users.name, blogs.user_id, blogs.title 
FROM users 
CROSS JOIN  blogs;
+----+------+---------+-------+
| id | name | user_id | title |
+----+------+---------+-------+
|  1 | w    |       1 | r     |
|  2 | x    |       1 | r     |
|  3 | y    |       1 | r     |
|  4 | z    |       1 | r     |
|  1 | w    |       2 | s     |
|  2 | x    |       2 | s     |
|  3 | y    |       2 | s     |
|  4 | z    |       2 | s     |
|  1 | w    |       3 | t     |
|  2 | x    |       3 | t     |
|  3 | y    |       3 | t     |
|  4 | z    |       3 | t     |
|  1 | w    |       1 | q     |
|  2 | x    |       1 | q     |
|  3 | y    |       1 | q     |
|  4 | z    |       1 | q     |
|  1 | w    |      10 | p     |
|  2 | x    |      10 | p     |
|  3 | y    |      10 | p     |
|  4 | z    |      10 | p     |
+----+------+---------+-------+
20 rows in set

也可以简写为:

SELECT users.id, users.name, blogs.user_id, blogs.title 
FROM users, blogs;

加上ON(或者WHERE):

mysql> SELECT users.id, users.name, blogs.user_id, blogs.title 
FROM users 
CROSS JOIN  blogs
ON users.id = blogs.user_id;
+----+------+---------+-------+
| id | name | user_id | title |
+----+------+---------+-------+
|  1 | w    |       1 | r     |
|  1 | w    |       1 | q     |
|  2 | x    |       2 | s     |
|  3 | y    |       3 | t     |
+----+------+---------+-------+
4 rows in set

LEFT JOIN

mysql> SELECT users.id, users.name, blogs.user_id, blogs.title 
FROM users 
LEFT JOIN  blogs
ON users.id = blogs.user_id;
+----+------+---------+-------+
| id | name | user_id | title |
+----+------+---------+-------+
|  1 | w    |       1 | r     |
|  2 | x    |       2 | s     |
|  3 | y    |       3 | t     |
|  1 | w    |       1 | q     |
|  4 | z    | NULL    | NULL  |
+----+------+---------+-------+

LEFT JOIN的过程如下(摘自《SQL编程风格》):

(1) 构造两个表的CROSS JOIN,扫描结果集中的每一行。

(2) 如果对于那一行的谓词测试为TRUE,则保留它。

(3) 如果对于那一行的谓词测试为FALSE或UNKNOWN,则保留表users的列保持不变,将非保留表blogs的所有列都转化为NULL,然后删去重复的。

基本原则保留表中的每一行在结果行中都至少出现一次。

users LEFT JOIN blogs意味着users中的每行都会在结果中出现(除非ON中还有其他的逻辑语句限制),blogs中符合要求的数据会出现。这里,users表称为保留表blogs表称为非保留表

LEFT JOINLEFT OUTER JOIN是一个意思。

LEFT ON + WHERE

在ON中可以添加多个条件语句进行过滤,例如:

mysql> SELECT users.id, users.name, blogs.user_id, blogs.title 
FROM users 
LEFT JOIN  blogs
ON users.id = blogs.user_id
AND blogs.user_id IS NULL;
+----+------+---------+-------+
| id | name | user_id | title |
+----+------+---------+-------+
|  1 | w    | NULL    | NULL  |
|  2 | x    | NULL    | NULL  |
|  3 | y    | NULL    | NULL  |
|  4 | z    | NULL    | NULL  |
+----+------+---------+-------+
4 rows in set

blogs.user_id IS NULL是的CROSS JOIN的结果据不符合要求,这也就导致了上面的运行结果。

而如果:

mysql> SELECT users.id, users.name, blogs.user_id, blogs.title 
FROM users 
LEFT JOIN  blogs
ON users.id = blogs.user_id
WHERE blogs.user_id IS NULL;
+----+------+---------+-------+
| id | name | user_id | title |
+----+------+---------+-------+
|  4 | z    | NULL    | NULL  |
+----+------+---------+-------+
1 row in set

可以看出WHERE是在LEFT ON生成结果后才进行的过滤。

RIGHT JOIN

就是LEFT JOIN的反转。

mysql> SELECT users.id, users.name, blogs.user_id, blogs.title 
FROM blogs 
RIGHT JOIN users
ON users.id = blogs.user_id;

+----+------+---------+-------+
| id | name | user_id | title |
+----+------+---------+-------+
|  1 | w    |       1 | r     |
|  2 | x    |       2 | s     |
|  3 | y    |       3 | t     |
|  1 | w    |       1 | q     |
|  4 | z    | NULL    | NULL  |
+----+------+---------+-------+
5 rows in set

INNER JOIN

mysql> SELECT users.id, users.name, blogs.user_id, blogs.title 
FROM users 
INNER JOIN  blogs;
+----+------+---------+-------+
| id | name | user_id | title |
+----+------+---------+-------+
|  1 | w    |       1 | r     |
|  2 | x    |       1 | r     |
|  3 | y    |       1 | r     |
|  4 | z    |       1 | r     |
|  1 | w    |       2 | s     |
|  2 | x    |       2 | s     |
|  3 | y    |       2 | s     |
|  4 | z    |       2 | s     |
|  1 | w    |       3 | t     |
|  2 | x    |       3 | t     |
|  3 | y    |       3 | t     |
|  4 | z    |       3 | t     |
|  1 | w    |       1 | q     |
|  2 | x    |       1 | q     |
|  3 | y    |       1 | q     |
|  4 | z    |       1 | q     |
|  1 | w    |      10 | p     |
|  2 | x    |      10 | p     |
|  3 | y    |      10 | p     |
|  4 | z    |      10 | p     |
+----+------+---------+-------+
20 rows in set

mysql> SELECT users.id, users.name, blogs.user_id, blogs.title 
FROM users 
INNER JOIN  blogs
ON users.id = blogs.user_id;

+----+------+---------+-------+
| id | name | user_id | title |
+----+------+---------+-------+
|  1 | w    |       1 | r     |
|  1 | w    |       1 | q     |
|  2 | x    |       2 | s     |
|  3 | y    |       3 | t     |
+----+------+---------+-------+
4 rows in set

由此可见INNER JOIN 和CROSS JOIN差不多,但是又有什么区别呢?先看下面这段话(来自官方文档):

In MySQL, JOIN, CROSS JOIN, and INNER JOIN are syntactic equivalents (they can replace each other). In standard SQL, they are not equivalent. INNER JOIN is used with an ON clause, CROSS JOIN is used otherwise.

也就是说,在MySQL中JOIN, CROSS JOIN 和 INNER JOIN这三个是一个意思。但是要注意到它们和LEFT JOIN的不同,第(3)步要修改为:

(3) 如果对于那一行的谓词测试为FALSE或UNKNOWN,删去该行。

FULL OUTER JOIN

MySQL不支持FULL OUTER JOIN,但是可以使用UNION模拟:

mysql> SELECT users.id, users.name, blogs.user_id, blogs.title 
FROM users 
LEFT JOIN blogs
ON users.id = blogs.user_id

UNION

SELECT users.id, users.name, blogs.user_id, blogs.title 
FROM users 
RIGHT JOIN blogs
ON users.id = blogs.user_id;


+------+------+---------+-------+
| id   | name | user_id | title |
+------+------+---------+-------+
|    1 | w    |       1 | r     |
|    2 | x    |       2 | s     |
|    3 | y    |       3 | t     |
|    1 | w    |       1 | q     |
|    4 | z    | NULL    | NULL  |
| NULL | NULL |      10 | p     |
+------+------+---------+-------+
6 rows in set

其他

如何查看表的创建语句:

SHOW CREATE TABLE <table name>;

参考

Full Outer Join in MySQL
13.2.8.2 JOIN Syntax
Join (SQL)
What's the difference between INNER JOIN, LEFT JOIN, RIGHT JOIN and FULL JOIN?
《SQL编程风格》 Joe Celko著



( 本文完 )