65.9K
CodeProject 正在变化。 阅读更多。
Home

SQL 连接

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.40/5 (31投票s)

2010年8月20日

CPOL

8分钟阅读

viewsIcon

201816

本文旨在介绍所有不同类型的 SQL 连接

我通常觉得人们对 SQL Server 中的连接(Join)感到害怕。但如果他们了解 SQL Server 中不同类型的连接是什么,以及如何最好地使用它们,他们会真正喜欢上使用连接。通过使用连接,我们可以根据一些逻辑条件从多个表中获取数据。

SQL Server 中不同类型的连接

  1. 内连接(Inner join)或等值连接(Equi join)
  2. 外连接(Outer Join)
  3. 交叉连接(Cross join)

假设我们有两个表,Employee(员工)和 Department(部门),其描述如下:-

CREATE TABLE [dbo]. [Employee](
[Empid] [Int] IDENTITY (1, 1) NOT NULL Primary key,
[EmpNumber] [nvarchar](50) NOT NULL,
[EmpFirstName] [nvarchar](150) NOT NULL,
[EmpLastName] [nvarchar](150) NULL,
[EmpEmail] [nvarchar](150) NULL,
[Managerid] [int] NULL,
[Departmentid] [INT]
)
CREATE TABLE [dbo].[Department](
[Departmenttid] [int] IDENTITY (1, 1) NOT NULL primary key,
[DepartmentName] [nvarchar](255) NOT NULL
)

创建表之后,我们需要向这些表中插入数据。使用以下查询来插入数据:-

insert into Employee
(EmpNumber,EmpFirstName,EmpLastName,EmpEmail,Managerid,Departmentid)
values('A001','Samir','Singh','samir@abc.com',2,2)
insert into Employee
(EmpNumber,EmpFirstName,EmpLastName,EmpEmail,Managerid,Departmentid)
values('A002','Amit','Kumar','amit@abc.com',1,1)
insert into Employee (EmpNumber,EmpFirstName,EmpLastName,EmpEmail,Managerid,Departmentid)
values('A003','Neha','Sharma','neha@abc.com',1,2)
insert into Employee (EmpNumber,EmpFirstName,EmpLastName,EmpEmail,Managerid,Departmentid)
values('A004','Vivek','Kumar','vivek@abc.com',1,NULL)

insert into Department(DepartmentName)
values('Accounts')
insert into Department(DepartmentName)
values('Admin')
insert into Department(DepartmentName)
values('HR')
insert into Department(DepartmentName)
values('Technology')

内连接(Inner Join)

这种类型的连接也称为等值连接(Equi join)。这种连接返回两个表中存在匹配的所有行。当我们需要仅选择在 ON 子句中指定的列中具有共同值的那些行时,可以使用这种类型的连接。

现在,如果我们想要获取那些至少属于一个部门的员工的员工ID、员工名字、员工姓氏及其部门名称,那么我们可以使用内连接。

内连接的查询

SELECT Emp.Empid, Emp.EmpFirstName, Emp.EmpLastName, Dept.DepartmentName 
 FROM Employee Emp 
  INNER JOIN Department dept 
     ON Emp.Departmentid=Dept.Departmenttid

结果

Empid EmpFirstName EmpLastName DepartmentName
1     Samir        Singh       Admin
2     Amit         Kumar       Accounts
3     Neha         Sharma      Admin

解释

在此查询中,我们基于“Departmentid”列(在“Employee”和“Department”两个表中都存在的公共列)使用了内连接。此查询将返回两个表中“Departmentid”列具有共同值的所有行。Neha Sharma 和 Samir Singh 在 Employee 表的 Departmentid 列中的值为“2”。在 Department 表中,部门“Admin”在 Departmentid 列中的值为“2”。因此,上述查询为“Admin”部门返回两行,一行是 Neha Sharma,另一行是 Samir Singh。

自连接(Self Join)

有时我们需要将一个表与其自身连接。这种类型的连接称为自连接(Self join)。它是内连接的一种,其中两个连接列都属于同一个表。在这种连接中,我们需要在内存中打开同一个表的两个副本。由于两个实例的表名相同,我们使用表别名来使同一个表的相同副本在不同的内存位置打开。例如,如果我们需要获取员工姓名及其经理姓名,我们就需要使用自连接,因为员工的经理ID也存储在与员工相同的表中。

自连接的查询

SELECT Emp1.Empid, 
       Emp1.EmpFirstName+' '+Emp1.EmpLastName as EmployeeName, 
    Emp2.EmpFirstName+' '+Emp2.EmpLastName as ManagerName 
  FROM Employee Emp1 
     INNER JOIN Employee Emp2 
    ON Emp1.Managerid=Emp2.Empid

结果

Empid   EmployeeName   ManagerName
1       Samir Singh    Amit Kumar
2       Amit Kumar     Samir Singh
3       Neha Sharma    Samir Singh
4       Vivek Kumar    Samir Singh

解释

由于员工和经理的信息都包含在同一个表(Employee 表,因为他们都是员工)中,我们必须使用自连接。在自连接查询中,我们通过使用别名 Emp1 和 Emp2 创建了 Employee 表的两个副本,然后通过使用 Emp1 的 managerid 列和 Emp2 表的 Empid 列在它们之间进行内连接。在此示例中,我们使用 Employee 表的 managerid 和 empid 列,因为员工的经理的员工ID存储在 Employee 表的 managerid 中。

外连接(Outer Join)

当我们需要从左侧(或右侧或两侧)的表中选择所有行,而不管另一个表是否有共同值时,就需要这种类型的连接,它通常会为缺失的数据输入 null 值。

外连接可以是三种类型

  1. 左外部联接
  2. 右外连接(Right Outer Join)
  3. 全外部联接

左外部联接

如果我们想获取所有员工的员工ID、员工名字、员工姓氏及其部门名称,而不管他们是否属于任何部门,那么我们可以使用左外连接。在这种情况下,我们将 Employee 表放在连接子句的左侧。它将为右表中缺失的数据插入 NULL 值。

左外连接的查询

SELECT Emp.Empid, 
       Emp.EmpFirstName, 
    Emp.EmpLastName, 
    Dept.DepartmentName
  FROM Employee Emp 
     LEFT OUTER JOIN Department dept 
     ON Emp.Departmentid=Dept.Departmenttid

结果

Empid EmpFirstName EmpLastName DepartmentName
1     Samir        Singh       Admin
2     Amit         Kumar       Accounts
3     Neha         Sharma      Admin
4     Vivek        Kumar       NULL

解释

由于我们使用了左外连接,此查询将从 Employee 表中给出所有员工的信息(员工ID、员工名字、员工姓氏及其部门名称),并在员工不属于任何部门的情况下,在 DepartmentName 列中插入 NULL 值。在 Employee 表中,由于 Samir Singh、Amit Kumar 和 Neha Sharma 在他们的 Departmentid 列中有值,因此上述查询将在 DepartmentName 标题下显示他们的部门名称。但由于 Vivek Kumar 不属于任何部门,并且在 Departmentid 列中为 null 值,因此上述查询将在 DepartmentName 列标题下显示 NULL 值。

右外连接(Right Outer Join)

如果我们想获取所有部门的名称以及属于该部门的所有员工的员工ID、员工名字和员工姓氏,而不管一个部门是否有员工,那么我们可以使用右外连接。在这种情况下,我们将 Department 表放在连接子句的右侧。它将为左表(Employee)中缺失的数据插入 NULL 值。

右外连接的查询

SELECT Dept.DepartmentName, 
       Emp.Empid, Emp.EmpFirstName, 
    Emp.EmpLastName 
  FROM Employee Emp 
    RIGHT OUTER JOIN Department dept 
   ON Emp.Departmentid=Dept.Departmentid

结果

DepartmentName Empid EmpFirstName EmpLastName
Accounts       2     Amit         Kumar
Admin          1     Samir        Singh
Admin          3     Neha         Sharma
HR             NULL  NULL         NULL
Technology     NULL  NULL         NULL

解释

由于我们使用了右外连接,此查询将根据 Departmentid 列中包含的值连接 Employee 和 Department 两个表。它将从 Department 表中给出部门名称,以及属于该部门的所有员工的员工ID、员工名字和员工姓氏。如果任何部门不包含任何员工,则它会在来自 Employee 表的列中插入 NULL 值。由于没有员工与 HR 和 Technology 部门相关联,此查询将在 HR 和 Technology 部门的 Empid、EmpFirstName 和 EmpLastName 列下显示 NULL 值。由于 Admin 和 Accounts 部门包含员工,因此 Empid、EmpFirstName 和 EmpLastName 列分别包含员工ID、员工名字和员工姓氏的信息。

全外部联接

如果我们想获取所有部门的名称以及所有员工的员工ID、员工名字、员工姓氏,而不管一个部门是否有员工,或者一个员工是否属于一个部门,那么我们可以使用全外连接。它将为两个表中缺失的数据插入 null 值。

全外连接的查询

SELECT Emp.Empid, 
       Emp.EmpFirstName, 
    Emp.EmpLastName, 
    Dept.DepartmentName 
  FROM Employee Emp 
     FULL OUTER JOIN Department dept 
    ON Emp.Departmentid=Dept.Departmenttid

结果

Empid EmpFirstName EmpFirstName DepartmentName
1     Samir        Singh        Admin
2     Amit         Kumar        Accounts
3     Neha         Sharma       Admin
4     Vivek        Kumar        NULL
NULL  NULL         NULL         HR
NULL  NULL         NULL         Technology

解释

由于我们使用了全外连接,此查询将从 Department 表中给出所有部门的名称,并从 Employee 表中给出所有员工的员工ID、员工名字、员工姓氏。如果任何部门不包含任何员工,则它会在 Empid、EmpFirstName、EmpLastName 列中插入 NULL 值;如果任何员工不属于任何部门,则它会在 DepartmentName 列中插入 NULL 值。在这里,由于 Vivek Kumar 不属于任何部门,结果在 DepartmentName 列下显示 NULL 值。由于 HR 和 Accounts 部门不包含任何员工,上述查询的结果在 HR 和 Technology 部门的 Empid、EmpFirstName 和 EmpLastName 列下显示 NULL 值。

交叉连接(Cross Join)

这种连接将左表中的所有行与右表中的每一行进行组合。当我们需要从两个表中选择所有可能的行和列的组合时,就需要这种类型的连接。这种连接通常不被推荐,因为它需要很长时间并产生一个通常无用的巨大结果。

交叉连接的查询

SELECT Emp.Empid,
       Emp.EmpFirstName,
       Emp.EmpLastName,
    Dept.DepartmentName 
 FROM Employee Emp 
   CROSS JOIN Department dept

结果

Empid EmpFirstName EmpLastName DepartmentName
1     Samir        Singh       Accounts
2     Amit         Kumar       Accounts
3     Neha         Sharma      Accounts
4     Vivek        Kumar       Accounts
1     Samir        Singh       Admin
2     Amit         Kumar       Admin
3     Neha         Sharma      Admin
4     Vivek        Kumar       Admin
1     Samir        Singh       HR
2     Amit         Kumar       HR
3     Neha         Sharma      HR
4     Vivek        Kumar       HR
1     Samir        Singh       Technology
2     Amit         Kumar       Technology
3     Neha         Sharma      Technology
4     Vivek        Kumar       Technology

解释

这个交叉连接查询将把 Employee 表中的所有行与 Department 表中的每一行进行组合。由于 Employee 表包含4行,Department 表包含4行,因此这个结果将返回 4*4=16 行。此查询不包含任何 ON 子句。

总结

以上讨论可以总结为:连接用于在单个查询中从多个表中选择数据。内连接用于仅选择那些在连接所基于的列中具有共同值的行。左外连接用于从左侧表中选择行,而不管右侧表是否有共同值。同样,右外连接用于从右侧表中选择行,而不管左侧表是否有共同值。交叉连接用于从两个表中获取所有可能的行和列组合。只有在没有其他办法时才应使用它,因为它可能会运行很长时间并返回一个可能无用的巨大结果集。

如果连接是基于具有很少唯一值的列,则应避免表的连接。为了提高连接性能,最好通过在查询中包含 WHERE 子句来限制需要连接的行数。如果用于连接表的列有自己的索引,连接性能也可以提高。

© . All rights reserved.