SQL学习之联结表的使用

时间:2022-04-24
本文章向大家介绍SQL学习之联结表的使用,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

1、简介:"联结(join)表"是SQL最强大的功能之一。联结是利用SQL的SELECT能执行的最重要的操作,很好地理解联结及其语法是学习SQL的极为重要的部分!

在能够有效的使用联结前,必须了解关系表以及关系型数据库设计的一些基础知识。

2、关系表

下面通过一个列子来理解关系表。

有一个包含产品目录的数据库表,其中每类物品占一行。对于每一种物品,要存储的信息包括产品描述、价格以及生产该产品的供应商。关于供应商,现在一个供应商生产多个产品,那么在何处存储供应商名、地址、联系方式等供应商信息呢?这里正确的做法是将供应商的信息和产品信息分开存储到两个表中,而分开的理由是:

(1)同一供应商生产的每个产品,其供应商信息是相同的,对每个产品重复此信息既浪费时间又浪费存储空间;

(2)如果供应商信息发生变化,只需修改一次供应商信息即可,而不需要去每个产品的信息中修改;

(3)如果有重复数据(即每种产品都存储供应商信息),则很难保证每次输入该数据的方式(也就是说输错供应商信息)都相同。不一致的数据在报表中就很难利用到;

关键是,相同的数据出现多次绝对不是一件好事,这是关系型数据库设计的基础。关系表的设计就是要把信息分解成多个表,一类数据一个表。各表之间通过某些共同的值相互关联(所以才叫关系型数据库,大多数情况下采用主键关联);

综上所述,我们建立两个表:一个存储供应商信息(Vendors),另一个存储产品信息(Products),Products表通过存储Vendors的主键实现两个表之间的通信(关联)。

这样做的好处是:

(1)供应商的信息不会重复,不会浪费时间和存储空间,每个产品只需要存储一个供应商Id,就可以通过它知道所有关于供应商的信息(前提是供应商Id要是唯一的);

(2)如果供应商的信息发生变动,只需要更新Vendors(供应商)表,相关表的数据不用改动;

(3)由于数据不重复,数据显然是一致的,使得处理数据和生成报表更简单;

总之,关系型数据库可以有效的存储,方便的处理。因此,关系型数据库的可伸缩性远比非关系数据库的要好。

可伸缩性:能够适应不断增加的工作量而不失败。设计良好的数据库或应用程序称为可伸缩性好。

3、像2中所说的将数据分解成多个表能有效的存储,更方便的处理,并且可伸缩性更好。但这些好处是有代价的。

    因为如果数据存储在多个表中,怎样用一条SELECT语句就检索出想要的数据呢?这个时候就需要使用到SQL的联结表技术了,简答的说,联结是一种机制,用来在一条SELECT语句中关联多个表,因此称为联结。使用特殊的语法,可以联结多个表返回一组输出,联结在运行时关联表中正确的行。

下面通过代码来理解联结的作用:

create database Study
go
use Study
go

create table Products
(
Id int identity(1000,1),
Name varchar(50) null,
Price decimal(4,1) null,
VendorId int null
)
insert into Products values('黑曼巴',666.6,100000)
insert into Products values('淘宝网',100.6,100001)
insert into Products values('人工智能汽车',366.6,100002)
insert into Products values('英雄联盟',166.6,100005)
insert into Products values('万达广场',66.6,100006)
select * from Products


create table Vendors
(
Id int identity(100000,1),
Name varchar(20) null,
Adress varchar(255) null,
Tel varchar(11) null
)
insert into Vendors values('Nick','America California','1111111111')
insert into Vendors values('Alibaba','Hangzhou China','2222222222')
insert into Vendors values('BaiDu','BeiJing China','3333333333')
insert into Vendors values('Tencent','ShenZheng China','44444444444')
insert into Vendors values('WanDa','DaLian China','5555555555')
select * from Vendors

这是关系库和关系表的SQL代码!

这是两个表的数据图,现在有个报表程序需要所有产品的详细信息,包括产品的名称、价格、供应商名称、供应商的地址等;

简单的分析下问题,我们发现产品的名称、价格、可以从Products表中获取,但是供应商名称、供应商的地址却需要从Vendors表中获取!这个时候我们就需要使用SQL的"联结表技术"了,下面是解决代码:

select Vendors.Name,Vendors.Adress,Products.Name,Products.Price from Vendors,Products WHERE Vendors.Id=Products.VendorId

ok,完成需求!

      下面分析下上面那段代码:首先SELECT语句和之前随笔中的SELECT语句都一样,即指定要检索的列,这里最大的差别是所指定的两列(Products.Name,Products.Price)在Products表中,而列外两列(Vendors.Name,Vendors.Adress)却在另一个表(Vendors)中,所以FROM语句和之前随笔中的不同,这里的FROM子句列出了两个表(Products表和Vendors表),最后再看WHERE子句,这里WHERE子句的作用是只是DBMS将Vendors表中的Id与Products表中的VendorId进行匹配,即Vendors表中每一行将于Products表中的每一行进行条件判断,条件是Vendors表的Id和Products表的VendorId是否相等,如果相等的话,再将两条记录进行合并(这是我个人的假想),通过SELECT语句返回对应的记录,如果不相等,则将对应的行记录过滤!

注意:如果不指定WhERE子句,相当于没有联结条件(过滤条件),那么返回的结果就是笛卡尔积,检索出的行数等于(A表的行数乘以B表的行数,从上面的流程图就可以看出)。代码如下:

select Vendors.Name,Vendors.Adress,Products.Name,Products.Price from Vendors,Products 

4、内联结

     像上面例子中的两个表的数据联结成为等值联结,它基于两个表之间的相等测试,这种联结也称为内联结,其实,可以对上面列子中的两个表之间的联结使用稍微不同的语法,明确指定联结的类型,也能完成同样的效果。下面我们通过使用内联结的语法,来获取上面列子想要的数据。代码如下:

select a.Name,a.Adress,b.Name,b.Price from Vendors a INNER JOIN Products b ON a.Id=b.VendorId

ok,完成需求!通过内联结 INNER JOIN

比较一下两种实现代码发现:两端代码的SELECT语句是一样的,但FROM子句不同,内联结的FROM子句是告诉DBMS两张表的联结方式是INNER JOIN(内联结),联结的条件是ON 子句而不是WHERE子句这也是区别于上面列子的,但实际内联结的ON 子句与WHERE子句的作用是一样的。

5、联结多个表

SQL不限制一条SELECT 语句中可以联结的表数目,但事实上,许多DBMS对此都有限制,因为DBMS在运行时关联指定的每个表,以处理联结。这种处理非常耗资源,因此在实际的开发中,应该要注意,不要联结不必要的表,联结的表越多,性能下降就越厉害!例如:

select * from 
         dbo.Products,
         dbo.Vendors,
         dbo.OrderItems 
         where Vendors.Id=dbo.Products.VendorId 
           and dbo.OrderItems.ProductId=dbo.Products.Id 
           and dbo.OrderItems.OrderId=1

这个列子显示了订单编号为1的详细信息;

通过内联结在实现一次这个需求:

select * from
         dbo.Products INNER JOIN 
         dbo.Vendors ON Vendors.Id=dbo.Products.VendorId INNER JOIN
         dbo.OrderItems ON dbo.OrderItems.ProductId=Products.Id
         where dbo.OrderItems.OrderId=1