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. zookeeper 分布式协调服务

    分布式协调服务作用是将多机协调的职责从分布式应用中独立出来,以减少系统的耦合性和增加扩展性. 而zookeeper采用分布式中经典的主从架构:master->slave,通常以动态的存储分布式应 ...

  2. linux中文件属性

    一. linux系统如何管理文件 1.1. 硬盘中的静态文件和inode a. 文件平时都在存放在硬盘中的,硬盘中存储的文件以一种固定的形式存放的,我们叫静态文件. b. 一块硬盘中可以分为两大区域: ...

  3. tableview(model base)

    该tableview控件的用法: 1.原理: 数据存放在“表”对象中,而将这个对象关联到tableview控件之后,将实现在UI中展示出来. class Query_Students : public ...

  4. PC端微信防撤回功能分析

    1.打开PC端微信的安装目录,有一个WeChatWin.dll文件,微信的所有功能基本上都在这个文件中了 2.OD打开,搜索字符串revokemsg(撤回消息,掌握一门外语是多么的重要啊!!!),在所 ...

  5. Codeforces 1262D Optimal Subsequences(BIT+二分)

    首先比较容易想到肯定是前k大的元素,那么我们可以先对其进行sort,如果数值一样返回下标小的(见题意),接下里处理的时候我们发现需要将一个元素下标插入到有序序列并且需要访问第几个元素是什么,那么我们可 ...

  6. JS事件流、事件监听、事件对象、事件委托

    JS事件流: 01.DOM级别和DOM事件 02.JS事件流:页面中接收事件的顺序 事件冒泡阶段-->处于目标阶段-->事件捕获阶段 (事件捕获总发生在事件冒泡前面) 03.捕获:从外向里 ...

  7. Vue 变量,成员,属性监听

    Vue变量 <!DOCTYPE html> <html lang="zh"> <head> <meta charset="UTF ...

  8. 用eclipse怎么打war包?

    用eclipse怎么打war包? 在服务器上部署很多都是用war包进行部署的,eclipse是很友好的支持把java项目打成war包的,下面就把打war的经验写出来,供大家参考 百度经验:jingya ...

  9. iOS APP 国际化

    pp Store 中很多流行的应用程序有多种语言版本.虽然这些应用程序可能因为很多因素而变得流行,但是具有多种本地化版本,肯定是其中一个因素.越多的人可以理解并使用您的应用程序,潜在的买家也就越多. ...

  10. C 调试 gdb常用命令

    gdb常用命令: [root@redhat home]#gdb 调试文件:启动gdb (gdb) l :(字母l)从第一行开始列出源码 (gdb) break n :在第n行处设置断点 (gdb) b ...