對 7 種 SQL JOIN 查詢的總結, 準備表和數據 SQL :
create table person (
p_id bigint unsigned not null auto_increment ,
last_name varchar(30) not null ,
first_name varchar(30) not null ,
address varchar(100) not null ,
city varchar(30) not null ,
primary key (p_id)
) engine = InnoDB , charset = utf8 ;
insert into person (last_name, first_name, address, city)
values ('Tom' , 'John' , 'Oxford Street' , '倫敦') ,
('Bush' , 'George' , 'Fifth Avenue' , '紐約') ,
('Carter' , 'Thomas' , 'Changan Street' , '北京') ;
create table orders(
o_id bigint unsigned not null auto_increment ,
order_no bigint unsigned not null ,
p_id bigint unsigned not null ,
primary key (o_id)
) engine = innoDB , charset = utf8 ;
insert into orders (o_id, order_no, p_id)
values (NULL , 77895 , 3 ) ,
(NULL , 44678 , 3 ) ,
(NULL , 22456 , 1 ) ,
(NULL , 24562 , 1 ) ,
(NULL , 34764 , 66 ) ;
select * from person ;
select * from orders ;
內連接 (INNER JOIN)
內連接嚴格按照連接條件獲取 A 表 , B 表的交集。
select * from person
inner join orders
on person.p_id = orders.p_id ;
帶有查詢條件的內連接是可以正常執行的 ,下面這兩條 SQL 的效果是一樣的。
select * from person
inner join orders
on person.p_id = orders.p_id
and orders.order_no = 34764 ;
select * from person
inner join orders
on person.p_id = orders.p_id
where orders.order_no = 34764 ;
select * from person
inner join orders
on person.p_id = orders.p_id
and person.city = '北京' ;
這樣的 SQL 也是合法的 :
select * from person
inner join orders
on person.p_id = orders.p_id
and orders.order_no = 34764
where person.city = '北京' ;
on 關鍵字後面的查詢條件和 on 關鍵字共同構成了連接條件。所以連接條件不僅僅是滿足 on 的查詢條件即可, 要滿足 on 的條件以及之後所有的 and 或者 or 條件 。
左連接 (LEFT JOIN)
直觀的說左連接會獲取到左表中的所有數據,無論左表中的數據是否符合連接條件。那麼 SQL 語句中的那個表是左表呢 ?
select * from A left join B on ... 。 這個語句中 A 就是左表 ,也就是 from 關鍵字後面的表就是左表。
select * from person
left join orders
on person.p_id = orders.p_id ;
p_id = 2 的這條記錄只有在 person 表中才有 ,orders 表中是沒有 p_id = 2 的記錄的 。 查詢到的結果集中 orders 表中沒有數據的列都用 NULL 補齊了 。
這個 SQL 是合理的 and orders.o_id = 3 起到了控制右表結果集的效果 。
select * from person
left join orders
on person.p_id = orders.p_id
and orders.o_id = 3;
下面這個 SQL and person.p_id = 2 並沒有起到作用。結果集由 on person.p_id = orders.p_id 控制。
select * from person
left join orders
on person.p_id = orders.p_id
and person.p_id = 2 ;
下面這兩個 SQL 的結果是一致的。可以發現 and person.p_id = 2 並沒有起到作用。 但是同樣的條件出現在 where 後面時就起作用了。
select * from person
left join orders
on person.p_id = orders.p_id
where person.p_id = 2 ;
select * from person
left join orders
on person.p_id = orders.p_id
and person.p_id = 2
where person.p_id = 2 ;
可見 left join on 之後的 and 是右表的條件時是有效的 , and 是左表的條件時是無效的 , 但是也起到了連接條件的作用 ,也是屬於連接條件的一部分,會影響結果集的行數。最壞的情況是 on 關鍵字後的條件不成立 , on 之後 and 的條件不成立 ,得到的記錄數就是左表的記錄數。
右連接 (RIGHT JOIN)
右連接會取到右表的記錄 , 即使該記錄不符合連接條件。
select * from person
right join orders
on person.p_id = orders.p_id ;
在上面的 SQL 中 orders 表是"右表" , 也就是說 right join 關鍵字後的表是"右表" 。"右表"中的記錄會查詢出來,左表中不存在的記錄都會用 NULL 補齊。
下面這樣的 SQL 是合法有效的 。 能過過濾掉左表的記錄,但是不能影響到查詢出記錄的行數。
select * from person
right join orders
on person.p_id = orders.p_id
and person.city = '北京';
下面的 SQL 使用的時候要小心了 , 得到的結果和我們預期的並不一樣 , and orders.o_id = 1 , 這條語句並不能起到過濾 "右表" (orders 表)的作用 , 反而會影響到查詢"左表"的數據。
select * from person
right join orders
on person.p_id = orders.p_id
and orders.o_id = 1 ;
看到查詢的結果 , 除了 o_id = 1 , 行的左表記錄被保留了下來 ,其餘行的左表記錄都被拋棄了 , 但是總的結果集記錄數以及"右表"數據是沒有受到影響的。
全連接 (FULL JOIN)
MySQL 中不支持 full join 這樣的語句 , 所以只能使用 union , union all 。全連接就是獲取到左、右兩表的並集。
union 是將兩個查詢結果集合並起來, union 的特點是會去除完全重複的記錄 。 union all 不會去除完全重複的記錄,會獲取到重複的數據。
select * from person
left join orders
on person.p_id = orders.p_id
union all
select * from person
right join orders
on person.p_id = orders.p_id ;
可以看到結果集中用藍色框線圈住的兩個部分同一條記錄數據是完全一致的。
union 是去除了完全一致的重複數據的結果集。
select * from person
left join orders
on person.p_id = orders.p_id
union
select * from person
right join orders
on person.p_id = orders.p_id ;
LEFT JOIN EXCLUDING INNER JOIN
看圖很直觀就是左表中排除了和右表的交集部分剩下的記錄。
select * from person
left join orders
on person.p_id = orders.p_id
where orders.o_id is null ;
RIGHT JOIN EXCLUDING INNER JOIN
看圖很直觀就是右表中排除了和左表的交集部分剩下的記錄。
select * from person
right join orders
on person.p_id = orders.p_id
where person.p_id is null ;
FULL OUTER JOIN EXCLUDING INNER JOIN
全連接排除了兩表的交集剩下的記錄。MySQL 中還是使用 union , union all 語句。
select * from person
left join orders
on person.p_id = orders.p_id
where orders.o_id is null
union
select * from person
right join orders
on person.p_id = orders.p_id
where person.p_id is null ;
所以說 SQL JOIN 查詢總共就包含以上 7 種類型 , 7 種 JOIN 類型的總結圖 :