Chapter 3 Joins

Cross Joins(交叉联接)就是返回两个表的笛卡尔积(m行的表cross join一个n行的表得到一个m * n行的结果),它有两种标准SQL语法,第一种:

SELECT C.custid, E.empid
FROM Sales.Customers AS C
CROSS JOIN HR.Employees AS E;

第二种:

SELECT C.custid, E.empid
FROM Sales.Customers AS C, HR.Employees AS E;

你也可以对同一个表的多个实例进行联接,这种self join对cross joins, inner joins, 和outer join都适用。有这么一个技巧:首先创建一个表Digits,它只有一列digit,值是0到9,然后如果你要输出1到1000,那么可以这样:

SELECT D3.digit * 100 + D2.digit * 10 + D1.digit + 1 AS n
FROM dbo.Digits AS D1
CROSS JOIN dbo.Digits AS D2
CROSS JOIN dbo.Digits AS D3
ORDER BY n;

Inner Joins(内联接)有两个逻辑上的处理阶段:首先算出笛卡尔积,然后根据你指定的谓词对结果的行进行过滤。它同样有两种标准SQL的语法,第一种:

SELECT E.empid, E.firstname, E.lastname, O.orderid
FROM HR.Employees AS E
JOIN Sales.Orders AS O --因为inner join是默认,所以INNER可以省略
ON E.empid = O.empid;

对这种一对多的内联接,其实可以想成就是对每一行employee都对应很多行有相同employee ID的orders行。这里的ON与WHERE和HAVING一样,只接受TRUE而不接受FALSE或UNKNOWN。

第二种语法:

SELECT E.empid, E.firstname, E.lastname, O.orderid
FROM HR.Employees AS E, Sales.Orders AS O
WHERE E.empid = O.empid;

其实只是把第一种语法的ON移到了WHERE里面去,本质都一样。对于Cross Joins和Inner Joins,作者都建议第一种语法,因为意图更清晰,不容易出错。

连接条件也可以是多个的,例如:

FROM dbo.Table1 AS T1
JOIN dbo.Table2 AS T2
ON T1.col1 = T2.col1
AND T1.col2 = T2.col2

一般运用于这种情况:有个一对多的关系,而“一”的表的主键是基于多个列的,比如(orderid, productid),然后那个“多”的表肯定就要有一个外键来引用这个“一”的表的主键了对吧?(外键也能基于不止一个列)。我的理解是:如果在外键的那个表中如果添加了一行,外键的那两个列(比如就两列)的值必须是主键的那个表中已经存在的任意一行的那两个列的值,所以我推测字段的顺序必须一样,必须:FOREIGN KEY(orderid, productid) REFERENCES Sales.OrderDetails(orderid, productid),而不能FOREIGN KEY(productid, orderid) REFERENCES Sales.OrderDetails(orderid, productid)。所以在联接的时候就要这样:ON OD.orderid = ODA.orderid AND OD.productid = ODA.productid)。

联接条件也可以不是等于号:

SELECT
E1.empid, E1.firstname, E1.lastname,
E2.empid, E2.firstname, E2.lastname
FROM HR.Employees AS E1
JOIN HR.Employees AS E2
ON E1.empid < E2.empid;

这样的话会去掉self pairs (for example,1 with 1)和mirrored pairs (for example, 1 with 2 and also 2 with 1),于是得到雇员id之间的唯一的一对一配对。

Outer Joins(外联接)

有三种,虽然后两种都不常用:LEFT OUTER JOIN,RIGHT OUTER JOIN, FULL OUTER JOIN。RIGHT就是保留右边的全部的行,FULL就是左右两边的表都保留,OUTER可以省略。外联接相比内联接又多加了一步的处理,就是在笛卡尔积并且过滤结果后,又把要保留的那个表的一些行加进来了,这些行我们叫它outer row(外部行)。

如果你要在外联接的结果中选出所有外部行,就:

SELECT C.custid, C.companyname
FROM Sales.Customers AS C
LEFT OUTER JOIN Sales.Orders AS O
ON C.custid = O.custid
WHERE O.orderid IS NULL;

注意,这里除了可以选择O.orderid(因为它用作ON上)作为WHERE中的条件,也可以选择在不被保留的表(这里就是Sales.Orders)里面不为NULL的字段,或者主键(因为主键不为NULL)。

打印出介于某两个日期之间的所有日期:

SELECT DATEADD(day, n-1, '20060101') AS orderdate
FROM dbo.Nums
WHERE n <= DATEDIFF(day, '20060101', '20081231') + 1
ORDER BY orderdate;

这里的dbo.Nums是1,2,3,4,5,6……越大越好。

如果想查出Orders表中,对于以上日期的每一天的订单情况(包括没有订单的情况):

SELECT DATEADD(day, Nums.n - 1, '20060101') AS orderdate,
O.orderid, O.custid, O.empid
FROM dbo.Nums
LEFT OUTER JOIN Sales.Orders AS O
ON DATEADD(day, Nums.n - 1, '20060101') = O.orderdate
WHERE Nums.n <= DATEDIFF(day, '20060101', '20081231') + 1
ORDER BY orderdate;

其实这里的日期计算函数只是为了把连续的Nums映射到连续的日期,然后对每一个日期都保留。

关于外联接有个常见的错误,就是如果你在WHERE中用非保留的表中的列作为过滤条件的话,那么就等于内联接,例如:

SELECT C.custid, C.companyname, O.orderid, O.orderdate
FROM Sales.Customers AS C
LEFT OUTER JOIN Sales.Orders AS O
ON C.custid = O.custid
WHERE O.orderdate >= '20070101';

这样的话会把所有orderdate为NULL的外部行也过滤掉,那你还不如改成Inner join。

另一种常见错误是:

SELECT C.custid, O.orderid, OD.productid, OD.qty
FROM Sales.Customers AS C
LEFT OUTER JOIN Sales.Orders AS O
ON C.custid = O.custid
JOIN Sales.OrderDetails AS OD
ON O.orderid = OD.orderid;

注意,第一次Outer Join之后,可能会有orderid是null的行,结果第二次inner join之后,这些行就绝逼被过滤掉了。解决方法是要么就两次outer join,要么就先inner join后面两个表,然后在right outer join第一个表,或者也可以把“inner join后面两个表”用括号括起来,再用第一个表Left join它们。

另一个常见错误:

SELECT C.custid, COUNT(*) AS numorders
FROM Sales.Customers AS C
LEFT OUTER JOIN Sales.Orders AS O
ON C.custid = O.custid
GROUP BY C.custid;

还记得COUNT(*)会返回包括NULL的列吗,所以说这里就算有个顾客没有订单,也会算他的订单是一个。所以说记得用COUNT(O.orderid)。

《SQL Server 2012 T-SQL基础》读书笔记 - 3.联接查询的更多相关文章

  1. SQL Server Window Function 窗体函数读书笔记二 - A Detailed Look at Window Functions

    这一章主要是介绍 窗体中的 Aggregate 函数, Rank 函数, Distribution 函数以及 Offset 函数. Window Aggregate 函数 Window Aggrega ...

  2. SQL Server 2012:SQL Server体系结构——一个查询的生命周期(第1部分)

    为了缩小读取操作所涉及范围,本文首先着眼于简单的SELECT查询,然后引入执行更新操作有关的附加过程.最后你会读到,优化性能时SQLServer使用还原工具的相关术语和流程. 关系和存储引擎 如图所示 ...

  3. SQL Server 2012:SQL Server体系结构——一个查询的生命周期(第2部分)

    计划缓存(Plan Cache) 如果SQL Server已经找到一个好的方式去执行一段代码时,应该把它作为随后的请求重用,因为生成执行计划是耗费时间且资源密集的,这样做是有有意义的. 如果没找到被缓 ...

  4. SQL Sever 各版本下载 SQL Server 2012下载SQL Server 2008下载SQL Server 2005

    SQL Server 2012SQL Server 2012 开发版(DVD)(X64,X86)(中文简体)ed2k://|file|cn_sql_server_2012_developer_edit ...

  5. sql server 2012 导出sql文件

    导出表数据和表结构sql文件 在工作中,经常需要导出某个数据库中,某些表数据:或者,需要对某个表的结构,数据进行修改的时候,就需要在数据库中导出表的sql结构,包括该表的建表语句和数据存储语句!在这个 ...

  6. SQL Server Window Function 窗体函数读书笔记一 - SQL Windowing

    SQL Server 窗体函数主要用来处理由 OVER 子句定义的行集, 主要用来分析和处理 Running totals Moving averages Gaps and islands 先看一个简 ...

  7. SQL Server 2012:SQL Server体系结构——一个查询的生命周期(第3部分)(完结)

    一个简单的更新查询 现在应该知道只读取数据的查询生命周期,下一步来认定当你需要更新数据时会发生什么.这个部分通过看一个简单的UPDATE查询,修改刚才例子里读取的数据,来回答. 庆幸的是,直到存取方法 ...

  8. SQL Server 2012 - 动态SQL查询

    动态SQL的两种执行方式:EXEC @sql 和 EXEC sys.sp_executesql @sql DECLARE @c_ids VARCHAR(200) SET @c_ids ='1,2' - ...

  9. SQL Server 2012 - 数据库的基础操作

    数据库基本操作 --新建数据库卡 use master go create database SchoolDB on ( Name=SchoolDB, FileName='D;\DB\SchoolDB ...

随机推荐

  1. 【Linux-驱动】驱动策略----自旋锁

    自旋锁 自旋锁最多只能被一个内核任务持有.要是锁未被持有,请求它的内核任务便会立即得到它并继续执行.如果一个内核任务试图请求一个已经被别的内核任务持有的自旋锁,那么CPU就会一直尽心循环---旋转-- ...

  2. 获取程序所有加载的dll名称

    1.在任务管理器输入如下指令.输出到文件:tasklist /m >c:\dll.txt 输出到命令行 tasklist /m 2.使用工具软件https://files.cnblogs.com ...

  3. python列表的复制,扯一下浅拷贝与深拷贝的区别

    将一个列表的数据复制到另一个列表中.使用列表[:],可以调用copy模块 import copy A = [21,22,23,24,['a','b','c','d'],25,26] B = A #直接 ...

  4. SpringMVC简单介绍及执行

    SpringMVC介绍 Spring MVC是Spring提供的一个强大而灵活的web框架.借助于注解,Spring MVC提供了几乎是POJO的开发模式,使得控制器的开发和测试更加简单.这些控制器一 ...

  5. HTML第一课(前期知识准备)

    在正式的学习之前,我们先了解一些前端方面的常识. 一.前端是做什么? 如果有认真看过我写的预备程序员不得不知道的事儿这篇文章的同学应该清楚,前端的工作在整个项目开发中处于代码编写阶段,主要是用来做界面 ...

  6. Aniamtion加载动画

    css新增样式Animation的运用 希望可以通过这个案例加深对Animation以及Transform 两大api的认识 效果图如下: 在这里需要注意的是:理应通过发送服务器请求来获取图片,从而达 ...

  7. Django发送邮件功能

    以126邮箱为例 1 首先进126邮箱设置,开启: POP3/SMTP服务 IMAP/SMTP服务 成功开启后会获得一个授权码.   2. setting.py配置: # 配置发送邮箱 # 需要登录网 ...

  8. 下载xlsx文件打开一直提示文件已损坏

    这是office受保护视图导致的原因所造成的,按照以下操作,问题就不是问题了 解决办法如下: 1.在打开excel2018数据表格时,出现提示“文件已损坏,无法打开”,点击确定按钮 2.进入空白程序界 ...

  9. lnamp完整版[linux+apache2.4+php5.6.6+mysql5.6]

    Lnamp环境安装实录 将采用的开源软件: Apache [WEB动态脚本服务器,做nginx的反向代理 8080端口] Tengine [WEB静态文件服务器 80端口] MySQL PHP .Ap ...

  10. git-shell设置代理

    Configure Git to use a proxy ##In Brief You may need to configure a proxy server if you're having tr ...