SQL联结笔记(内联结,自联结,自然联结,外联结区别以及应用)
SQL中有三种联结,分别是:内联结,自然联结,外联结.
联结是针对不同表联合起来的一种方式.应用的对象是:表(table)
为了方便验证练习理解,首先展示所要用到的表的内容:
1.Customers表:
数据(可复制,创建表,插入数据):
CREATE TABLE Customers
(
cust_id char(10) NOT NULL ,
cust_name char(50) NOT NULL ,
cust_address char(50) NULL ,
cust_city char(50) NULL ,
cust_state char(5) NULL ,
cust_zip char(10) NULL ,
cust_country char(50) NULL ,
cust_contact char(50) NULL ,
cust_email char(255) NULL
);
-- ------------------------
-- Populate Customers table
-- ------------------------
INSERT INTO Customers(cust_id, cust_name, cust_address, cust_city, cust_state,
cust_zip, cust_country, cust_contact, cust_email)
VALUES('1000000001', 'Village Toys', '200 Maple Lane', 'Detroit', 'MI',
'44444', 'USA', 'John Smith', 'sales@villagetoys.com');
INSERT INTO Customers(cust_id, cust_name, cust_address, cust_city, cust_state,
cust_zip, cust_country, cust_contact)
VALUES('1000000002', 'Kids Place', '333 South Lake Drive', 'Columbus', 'OH',
'43333', 'USA', 'Michelle Green');
INSERT INTO Customers(cust_id, cust_name, cust_address, cust_city, cust_state,
cust_zip, cust_country, cust_contact, cust_email)
VALUES('1000000003', 'Fun4All', '1 Sunny Place', 'Muncie', 'IN', '42222',
'USA', 'Jim Jones', 'jjones@fun4all.com');
INSERT INTO Customers(cust_id, cust_name, cust_address, cust_city, cust_state,
cust_zip, cust_country, cust_contact, cust_email)
VALUES('1000000004', 'Fun4All', '829 Riverside Drive', 'Phoenix', 'AZ',
'88888', 'USA', 'Denise L. Stephens', 'dstephens@fun4all.com');
INSERT INTO Customers(cust_id, cust_name, cust_address, cust_city, cust_state,
cust_zip, cust_country, cust_contact)
VALUES('1000000005', 'The Toy Store', '4545 53rd Street', 'Chicago', 'IL',
'54545', 'USA', 'Kim Howard');
-- ----------------------
展示:
2.Vendors:
-- -------------------
-- Create Orders table
-- -------------------
CREATE TABLE Orders
(
order_num int NOT NULL ,
order_date datetime NOT NULL ,
cust_id char(10) NOT NULL
);
-- ----------------------
-- Populate Vendors table
-- ----------------------
INSERT INTO Vendors(vend_id, vend_name, vend_address, vend_city, vend_state,
vend_zip, vend_country)
VALUES('BRS01','Bears R Us','123 Main Street','Bear Town','MI','44444', 'USA');
INSERT INTO Vendors(vend_id, vend_name, vend_address, vend_city, vend_state,
vend_zip, vend_country)
VALUES('BRE02','Bear Emporium','500 Park Street','Anytown','OH','44333',
'USA');
INSERT INTO Vendors(vend_id, vend_name, vend_address, vend_city, vend_state,
vend_zip, vend_country)
VALUES('DLL01','Doll House Inc.','555 High Street','Dollsville','CA','99999',
'USA');
INSERT INTO Vendors(vend_id, vend_name, vend_address, vend_city, vend_state,
vend_zip, vend_country)
VALUES('FRB01','Furball Inc.','1000 5th Avenue','New York','NY','11111',
'USA');
INSERT INTO Vendors(vend_id, vend_name, vend_address, vend_city, vend_state,
vend_zip, vend_country)
VALUES('FNG01','Fun and Games','42 Galaxy Road','London', NULL,'N16 6PS',
'England');
INSERT INTO Vendors(vend_id, vend_name, vend_address, vend_city, vend_state,
vend_zip, vend_country)
VALUES('JTS01','Jouets et ours','1 Rue Amusement','Paris', NULL,'45678',
'France');
-- -----------------------
3.Products:
-- ---------------------
-- Create Products table
-- ---------------------
CREATE TABLE Products
(
prod_id char(10) NOT
NULL ,
vend_id char(10) NOT
NULL ,
prod_name char(255) NOT NULL ,
prod_price decimal(8,2) NOT NULL ,
prod_desc
text NULL
);
-- -----------------------
-- Populate Products table
-- -----------------------
INSERT INTO Products(prod_id, vend_id, prod_name, prod_price, prod_desc)
VALUES('BR01', 'BRS01', '8 inch teddy bear', 5.99, '8 inch teddy bear, comes
with cap and jacket');
INSERT INTO Products(prod_id, vend_id, prod_name, prod_price, prod_desc)
VALUES('BR02', 'BRS01', '12 inch teddy bear', 8.99, '12 inch teddy bear, comes
with cap and jacket');
INSERT INTO Products(prod_id, vend_id, prod_name, prod_price, prod_desc)
VALUES('BR03', 'BRS01', '18 inch teddy bear', 11.99, '18 inch teddy bear, comes
with cap and jacket');
INSERT INTO Products(prod_id, vend_id, prod_name, prod_price, prod_desc)
VALUES('BNBG01', 'DLL01', 'Fish bean bag toy', 3.49, 'Fish bean bag toy,
complete with bean bag worms with which to feed it');
INSERT INTO Products(prod_id, vend_id, prod_name, prod_price, prod_desc)
VALUES('BNBG02', 'DLL01', 'Bird bean bag toy', 3.49, 'Bird bean bag toy, eggs
are not included');
INSERT INTO Products(prod_id, vend_id, prod_name, prod_price, prod_desc)
VALUES('BNBG03', 'DLL01', 'Rabbit bean bag toy', 3.49, 'Rabbit bean bag toy,
comes with bean bag carrots');
INSERT INTO Products(prod_id, vend_id, prod_name, prod_price, prod_desc)
VALUES('RGAN01', 'DLL01', 'Raggedy Ann', 4.99, '18 inch Raggedy Ann doll');
INSERT INTO Products(prod_id, vend_id, prod_name, prod_price, prod_desc)
VALUES('RYL01', 'FNG01', 'King doll', 9.49, '12 inch king doll with royal
garments and crown');
INSERT INTO Products(prod_id, vend_id, prod_name, prod_price, prod_desc)
VALUES('RYL02', 'FNG01', 'Queen doll', 9.49, '12 inch queen doll with royal
garments and crown');
4.Orders
-- -------------------
-- Create Orders table
-- -------------------
CREATE TABLE Orders
(
order_num int NOT NULL ,
order_date datetime NOT NULL ,
cust_id char(10) NOT NULL
);
-- ---------------------
-- Populate Orders table
-- ---------------------
INSERT INTO Orders(order_num, order_date, cust_id)
VALUES(20005, '2012-05-01', '1000000001');
INSERT INTO Orders(order_num, order_date, cust_id)
VALUES(20006, '2012-01-12', '1000000003');
INSERT INTO Orders(order_num, order_date, cust_id)
VALUES(20007, '2012-01-30', '1000000004');
INSERT INTO Orders(order_num, order_date, cust_id)
VALUES(20008, '2012-02-03', '1000000005');
INSERT INTO Orders(order_num, order_date, cust_id)
VALUES(20009, '2012-02-08', '1000000001');
好吧,如上原始数据已经有了,现在我们来理解SQL几种联结之间的关系和细节.
1.为什么要使用联结:
有时候为了方便存储,我们会把数据分解为多个表.例如,现在某公司需要用到以下产品,别人对应的供应商和价格如下:
这一张表存了供应商的信息和产品以及价格.可以看到这个供应商给我们提供了两种产品:QQ和邮箱;
这两种产品都是出自于同一个供应商,因此我们存储数据的时候,"供应商的地址"和"联系人" 就被重复存储了".现实应用中,一个供应商可能提供的产品远远超过2个,
这就会给我录入数据和存储数据和更新数据带来很大的麻烦.譬如:这个提供多种产品的供应商的地址需要更新的时候
于是我们可以把张表分解成两个表,关联两个表主键(方便查询,也就是相关联的共同的信息),如下:
1.
2.
这样一来,后续要我更新某个供应商的地址和联系人的时候,只需要对表2中的对应内容进行更新就好了.这样分解表后的两个表更直观一些了,但是在我们处理查询数据的时候,就要关联两个表中的
信息了,操作的复杂度就增大了.按照我们常规的处理方式,
如果我们要查提供某一个产品的供应商的全部信息,那就要先到表2中把供应商的地址和联系人先查出来,然后再结合表1中对应产品的其他信息
这时我们一种方式是分步处理:
1,按照步骤来,首先在表1中查出对应产品的供应商
2.在表2中查出对应的供应商的地址和联系人信息
另外一种是,嵌套子查询,一步设置完查询
但是这两种方式,操作起来都不是太方便,在输入的时候可能要多输入一些条件和内容才能达到我们想要的结果.因此就有了联结的概念.
如最上面所说,联结有几种方式:内联结,自联结,自然联结,外联结
内联结(INNER JOIN):内联结也可以称为等值联结.例如:
如以上两个表格中,每个订单包含订单编号,客户ID,订单日期,在Orders表中存储为一行,各订单的物品存储在相关的OrdersItems表中.Orders表不存储顾客的信息,只存储顾客ID.顾客的实际信息存储在Customers
表中.
现在,假如需要列出订购物品"RGAN01"的所有顾客,应该怎样检索?
步骤为下:
1.检索包含物品RGAN01的所有订单的编号.
2.检索具有前一步骤列出的订单编号的所有顾客 的ID
3.检索前一步骤返回的所有顾客ID的顾客信息
方法一:以上每步可以单独作为一个查询来执行.可以把一个SELECT 语句返回的结果用于另外一条SELECT语句的WHERE子句.
方法二:也可以使用子查询把3个查询组合成一条语句
方法三:直接使用内联结来关联两个表直接查询.
方法一实现:
2.方法二实现:
3.方法三实现:
上面是通过 WHERE 语句来实现的,这里面等同于 INNER JOIN...ON (INNER JOIN基本上只用于联结两个表,不太适合多个表联结).
INNER JOIN ...ON 的使用方式
2.自联结
例如:现在要给Jim Jones同一公司的所有顾客发送一封信件.这个查询要求首先找出Jim Jones工作的公司,然后找出在该公司工作的顾客.下面是实现代码:
通过customers表可以看出,cust_contact 里面对应的 Jim Jones 的公司是,Fun4All,然后筛选出 Fun4All 公司的其他联系人和客户ID
以上是通过子查询的方式实现的.
先对对比一下自联结方式实现:
此处要非常小心一定是要c2来进行过滤
3.自然联结
无论何时对表进行联结,应该至少有一列不止出现在一个表中(被联结的列).标准的联结(前一课中介绍的内联结)返回所有数据,相同的列甚至多次出现.
自然联结排除多线出现,使每列只返回一次.
自然联结要求只能选择那些唯一的列,一般通过对一个表使用通配符(SELECT*),而对其他表的列使用明确的子集来完成.如:
在这个例子中,通配符只对第一个表使用,所有其他列明确列出,所以没有重复的列被检索出来.事实上,我们目前所学的每个内联结都是自然联结
4.外联结
许多联结将一个表中的行与另一个表中的行相关联,但有时候需要包含没有关联行的那些行.
例如:
A.对每个顾客下的订单进行计数,包括那些至今尚未下订单的顾客;
B.列出所有产品以及订购数量,包括没有人订购的产品
C.计算平均销售规模,包括那些至今尚未下订单的顾客.
这种联结包含了那些在相关表中没有关联行的行的联结方式就是外联结
例如:
外联结使用的时候,注意区分 LEFT OUTER JOIN 还是 RIGHT OUTER JOIN 就是设定展示哪边的表的所有行.另外还可以使用FULL OUTER JOIN