zoukankan      html  css  js  c++  java
  • 关于SQL表联接

    以SQL2008为例,Microsoft SQL Server 2008支持四种表运算符-JOIN,APPLY,PIVOT,UNPIVOT.JOIN表运算符是ANSI标准,而其他三种是T-SQL对标准的扩展。今天主要是了解有关JOIN表的操作。

    JOIN表运算符对两个输入表进行操作。联接有三种基本类型:交叉联接,内联接和外联结。这三种联接的区别是它们采用的逻辑查询处理步骤各不相同,每种联接都有一套不同的步骤。交叉联接只有一个步骤-笛卡尔积;内联接有两个步骤-笛卡尔积,过滤;外联结有三个步骤-笛卡尔积,过滤,添加外部行。

    学习使用的数据库是T-SQLFundamentals2008数据库,数据库创建代码分享:http://pan.baidu.com/s/1jGLBxm2

    1.交叉联接

    逻辑上,交叉联接是最简单的联接。交叉联接只实现一个逻辑查询步骤(笛卡尔积)。这一步是对输入的两个表进行操作, 把它们联接起来,生成二者的笛卡尔积。也就是将一个输入表的每行与另一个表的所有行进行匹配。如果一个表有m行,另一个表有n行,将得到m*n行的结果集。

    SQL-Server支持交叉联接的两种标准语法-ANSI SQL-92和ANSI SQL-89语法。这里推荐使用ANSI SQL-92,具体原因稍后解释。 

    在下载数据库代码后创建T-SQLFundametals2008数据库后,下面的例子对数据库中的Customers表和Employees表进行交叉联接查询(使用ANSI SQL-92语法),返回集合中的custid和empid:

    -- ANSI SQL-92语法

    USE TSQLFundamentals2008;

    SELECT C.custid, E.empid

    FROM Sales.Customers AS C

      CROSS JOIN HR.Employees AS E;

    -- ANSI SQL-89语法

    SELECT C.custid, E.empid

    FROM Sales.Customers AS C, HR.Employees AS E;

    这两种语法在逻辑和性能上没有区别,它们都是最新的SQL标准中不可缺少的一部分,而且SQL Server的最新版本也完全支持这两种语法。之所以推荐使用ANSI SQL-92语法,在解释完内联接后将会变的很清楚。

    自交叉联接:对同一个表的多个实例也可以进行联接,这种功能就是所谓的自联接(self-join),所有基本联接类型(交叉联接,内联接,外联接)都支持自联接。

    例:该查询生成员工配对的所有可能组合。

    -- Self Cross-Join

    SELECT

      E1.empid, E1.firstname, E1.lastname,

      E2.empid, E2.firstname, E2.lastname

    FROM HR.Employees AS E1

      CROSS JOIN HR.Employees AS E2;

    在自联接中,必须为表起别名。如果不为表指定别名,联接结果中的列名就会有歧义。

    2.内联接

    内联接要应用两个逻辑查询处理步骤:它首先像交叉联接一样,对两个输入表进行笛卡尔积运算:然后根据用户指定的谓词对结果进行过滤。和交叉联接一样,内联接也有两种标准语法:ANSI SQL-92和ANSI SQL-89。

      2.1:ANSI SQL-92语法

        使用ANSI SQL-92语法,需在两个表名之间指定 INNER JOIN关键字。INNER关键字是可选的,因为内联接是默认的联接方式,所以可以只单独指定JOIN关键字,但是     还是建议全部指定。用于进行过滤的谓词是一个称为ON子句的特别设定的语句中指定的,该谓词也称之为联接条件。

      例:对TSQLFundamentals2008数据库中Employees表和Orders表执行内联接运算,根据谓词条件 E.empid=O.empid对雇员和订单记录进行匹配:

      -- ANSI SQL-92

    USE TSQLFundamentals2008;

    SELECT E.empid, E.firstname, E.lastname, O.orderid

    FROM HR.Employees AS E

      JOIN Sales.Orders AS O

        ON E.empid = O.empid;

    因为SQL使用三值谓词逻辑(true,false,unknown),ON子句也只返回谓词结果为true的行,而不会返回谓词结果为false或者unknown的行,如果雇员没有相关的订单,则在过滤处理阶段会将这些与雇员相关的行过滤掉。

      2.2:ANSI SQL-89语法

      和交叉联接类似,内联接也可以使用ANSI SQL-89语法来表达。可以像交叉联接那样在表名之间放一个逗号,在查询的WHERE子句中定义条件,如:

      -- ANSI SQL-89

      SELECT E.empid, E.firstname, E.lastname, O.orderid

      FROM HR.Employees AS E, Sales.Orders AS O

      WHERE E.empid = O.empid;

      注意:ANSI SQL-89语法没有ON子句

      2.3:更安全的内联接

      强烈推荐使用ANSI SQL-92的联接语法,因为他在某些方面用起来更安全。假如你想写一条内联接查询,但不小心忘记指定联接条件。如果这时候使用的是ANSI SQL-92语法,查询语句将是无效的,如:

      -- Inner Join Safety

      SELECT E.empid, E.firstname, E.lastname, O.orderid

      FROM HR.Employees AS E

        JOIN Sales.Orders AS O;

      然而,如果使用ANSI SQL-89语法,忘了指定联接条件,那么这个查询仍然是有效的,但执行的确是一个交叉查询:

      SELECT E.empid, E.firstname, E.lastname, O.orderid

      FROM HR.Employees AS E, Sales.Orders AS O;

      因为查询没有失败,所以可能一时无法发现语句中隐藏的逻辑错误,最终导致用户在使用应用程序时得到的是错误的结果。对于简短的查询语句来说,程序员还不太可能忘记指定联接条件;但大多数的产品应用中的查询语句要比这复杂的多,会涉及多个表,过滤条件,以及其他查询元素。在这些复杂条件下,忘记指定联接条件的可能性也随之增加。

    3.外联接

    3.1:外联结基础

    与内联接和交叉联接不同,外联结是在ANSI SQL-92中才被引入的,因此它只有一种标准语法----在表名中间指定JOIN关键字,在ON子句中指定联接条件。外联结会应用内联接所应用的两个逻辑步骤:(笛卡尔积和ON过滤),此外还多了一个外联结特有的第三步:添加外部行。

    在外联结中,要把一个表标记为“保留的”表,可以在表名之间使用关键字LEFT OUTER JOIN,RIGHT OUTER JOIN,以及FULL OUTER JOIN,其中OUTER关键字是可选的。LEFT表示左边的表是保留的,RIGHT表示右边的表是保留的,FULL表示关键字左右两个表都是保留的。外联结的第三个步骤就是要识别保留表中按照ON条件在另一个表中找不到匹配的那些行,再把这些行添加到联接的前两个步骤所生成的结果集中;对于来自联接的非保留表的那些列,追加的外部行中的这些列则用NULL作为占位符。

    通过例子来理解是个好办法。以下查询根据客户ID和订单的客户ID 对Customers表和Orders表进行联接,并返回客户和他们的订单信息。该查询语句使用的联接类型是左外联接,所以查询结果也会返回那些没有发出任何订单的客户:

    -- Customers and their orders, including customers with no orders

    SELECT C.custid, C.companyname, O.orderid

    FROM Sales.Customers AS C

      LEFT OUTER JOIN Sales.Orders AS O

        ON C.custid = O.custid;

    从外联结保留表的角度看,可以认为外联结结果中的数据行包括两种:内部行和外部行。内部行指的是按照ON子句中能在联接的另一个表中找到匹配的那些行;外部行就是找不到匹配的那些行。使用外联结时,经常会为到底是查询的ON子句中,还是在where子句中指定联接条件而感到困惑。从外联结的保留表中的行来考虑这个问题,ON子句中的过滤条件不是最终的。换句话说,ON子句中的条件并不能最终决定保留表中部分行是否会在结果中出现,而只是判断是否能够匹配到另一边表中的某些行。所以,当需要表达一个非最终的条件时,就在ON子句中指定联接条件。当在生成外部行以后,要应用过滤器,而且希望过滤条件是最终的,就应该在where中指定条件。

    同样通过例子理解是个好办法:

    在上面介绍外联时以下查询结果中:

    -- Customers and their orders, including customers with no orders

    SELECT C.custid, C.companyname, O.orderid

    FROM Sales.Customers AS C

      LEFT OUTER JOIN Sales.Orders AS O

        ON C.custid = O.custid;

    我们发现结果中当custid为22时,对应的orderid为null,那么再添加一个条件,外联结查询且custid不为22的结果。

    如果我们在ON中指定条件

    SELECT C.custid, C.companyname, O.orderid

    FROM Sales.Customers AS C

      LEFT OUTER JOIN Sales.Orders AS O

        ON C.custid = O.custid AND C.custid<>22;

    发现结果和上面的结果一样,同样会出现custid=22的那条数据,如果在where子句中添加条件

    SELECT C.custid, C.companyname, O.orderid

    FROM Sales.Customers AS C

      LEFT OUTER JOIN Sales.Orders AS O

        ON C.custid = O.custid

    WHERE C.custid<>22;

    你就会发现custid=22的那条数据被过滤掉了,所以在外联结查询中添加条件是视情况而选择在ON子句中添加还是在WHERE子句中添加,所以建议在ON子句中只放关于两个表联接所需要的条件,如果要实现对结果集的再次过滤,请使用where子句。

    摘自sql server2008技术内幕

    虽然以前学习过SQL,但是还是感觉学习的不是很透彻,这里推荐一本书:microsoft sql server 2008技术内幕 t-sql语言基础!!!分享地址http://pan.baidu.com/s/1hqtplqO

    看完之后又会发现自己学到好多东西!大神绕路,新人共勉!

  • 相关阅读:
    python+soket实现UDP协议的客户/服务端中文聊天程序
    如何实现PyQt5与QML响应彼此发送的信号?
    用 eric6 与 PyQt5 实现python的极速GUI编程(系列04)---- PyQt5自带教程:地址簿(address book)
    用 eric6 与 PyQt5 实现python的极速GUI编程(系列03)---- Drawing(绘图)(3)-- 画线
    用 eric6 与 PyQt5 实现python的极速GUI编程(系列03)---- Drawing(绘图)(2)-- 画点
    用 eric6 与 PyQt5 实现python的极速GUI编程(系列03)---- Drawing(绘图)(1)-- 绘写文字
    仿百度壁纸client(六)——完结篇之Gallery画廊实现壁纸预览已经项目细节优化
    软件概要设计说明书—模板
    Android--Handler的用法:在子线程中更新界面
    在PreferenceAcitity中使用Fragement时避免额外的Left和RightPadding
  • 原文地址:https://www.cnblogs.com/wuding/p/4772928.html
Copyright © 2011-2022 走看看