SQL Server-外部联接基础回顾(十三)
前言
本节我们继续讲讲联接类型中的外部联接,本节之后我们将讲述有关联接性能以及更深入的知识,简短内容,深入的理解,Always to review the basics。
外部联接
外部联接又分为左外部联接和右外部联接,使用关键字分别是LEFT OUTER JOIN、RIGHT OUTER JOIN、FULL OUTER JOIN,在这里OUTER关键字时可选的。LEFT关键字表示保留左侧的行,RIGHT关键字表示保留右侧的行,FULL关键字表示左侧和右侧的行都保留。外部联接的第三个逻辑查询处理阶段识别保留表中基于ON谓词未能与另一个表匹配的行,此阶段添加这些行到前两个联接阶段生成的结果中,在这些外部行中,对于联接非保留侧的属性将使用NULL作为占位符。说了这么多,左外部联接就是以左表为基准,若右表满足条件则返回右侧的行,若不满足则返回NULL而非右侧的实际行数据,右外部联接同理。我们来看如下一个简单的例子
USE TSQL2012
GO SELECT C.custid, C.companyname, O.orderid
FROM Sales.Customers AS C
LEFT JOIN Sales.Orders AS O ON C.custid = O.custid

从上知,Sales.Customers表中的有一个客户没有任何订单,它的订单Id为22,通过左侧客户Id为22而右侧得到订单Id为NULL而得知。
超越外部联接基础知识
通过上述对外部联接的介绍,我们知道通过外部联接能够得到缺失值,也就是不满足条件则返回NULL。这里我们假设有如下一场景,我们需要查询Orders订单表中所有订单,要求确保在范围2006年1月1日至2008年12月31日中每天至少有一行输出,对于范围能具有的订单的日期不做任何操作,但希望输出中包含没有订单的日期,使用NULL标记作为订单属性占位符。我们第一步需要解决的是得到2006年1月1日至2008年12月31日的所有日期,上一篇我们讲过交叉联接,通过交叉联接我们生成了数字表,这个时候就派上用场了。
首先需要得到2006年1月1日至2008年12月31日之间间隔的天数,然后得到此间隔中的所有日期,如下:
USE TSQL2012
GO SELECT DATEADD(DAY, n - ,'') AS orderdate
FROM dbo.Nums
WHERE n <= DATEDIFF(DAY, '','') +
ORDER BY orderdate

接下来通过上述得到的连续日期与Sales.Orders表中的订单日期进行匹配,从而得到订单所有信息,利用左外部联接不满足条件则返回NULL。
USE TSQL2012
GO SELECT DATEADD(DAY, n - ,'') AS orderdate,O.orderid,
O.custid, O.empid
FROM dbo.Nums
LEFT JOIN Sales.Orders AS O
ON DATEADD(DAY, dbo.Nums.n - , '') = O.orderdate
WHERE dbo.Nums.n <= DATEDIFF(DAY, '','') +
ORDER BY orderdate

外部联接注意事项 (1)
(1)WHERE造成外部联接成为逻辑上的内部联接
我们看上述图中在订单日期等于2006-11-09时,此时orderid为NULL,此时我们首先来进行如下查询
USE TSQL2012
GO SELECT orderdate, orderid, custid, empid
FROM Sales.Orders
WHERE orderdate > ''
我们查询Sales.Orders表中订单日期大于2006-11-08订单信息,我们看下返回结果

此时我们发现订单日期为2006-11-09的订单信息没有,为何如此呢,因为利用WHERE子句它会过滤掉UNKNOWN即NULL值,为什么要讲这个呢,因为在外部联接中不满足条件的右表原本会返回NULL,但是若存在WHERE子句时,此时会导致所有外部行会被过滤掉,换句话说就是抵消了外部联接实际上在逻辑上就相当于是一个内部联接,这就在无意中造成了逻辑上的BUG。所以基于此我们可以得出如下结论:
结论:在外部联接中若利用WHERE子句会过滤掉NULL,会导致所有外部行被过滤掉,实际上会抵消外部联接,最终外部联接在逻辑上就成为了一个内部联接。
(2)多表联接造成外部联接成为逻辑上的内部联接
在进行多联接时比如说首先进行两个表的外部联接,紧接着后面跟了一个内部联接的第三个表,如果内部联接中子句的谓词对自外部联接非保留侧的属性和来自第三个表的属性进行比较,此时所有外部行都会被过滤掉。是什么意思呢,当利用外部联接时会可能返回外部行的NULL值,此时再利用内部联接,因为NULL与任何值进行比较都会生成UNKNOWN,所以此时UNKNOWN会被ON筛选过滤掉。同上也就抵消外部联接,造成外部联接成为逻辑上的内部联接。如下
USE TSQL2012
GO SELECT C.custid, O.orderid, OD.productid, OD.qty
FROM Sales.Customers AS C
LEFT JOIN Sales.Orders AS O ON O.custid = C.custid
INNER JOIN Sales.OrderDetails AS OD ON OD.orderid = O.orderid

一般来说,无论何种类型的外部联接后跟一个内部联接或是外部联接子查询,外部行都会被过滤掉,当然,这是假设联接条件对来自左侧的NULL标记和右侧的任意值进行比较。为了解决这种问题,我们可以通过如下三种方案来解决。
【1】将第二个内部联接替换为左外部联接。
USE TSQL2012
GO SELECT C.custid, O.orderid, OD.productid, OD.qty
FROM Sales.Customers AS C
LEFT JOIN Sales.Orders AS O ON O.custid = C.custid
LEFT JOIN Sales.OrderDetails AS OD ON OD.orderid = O.orderid

【2】首先使用内部联接,然后再使用右外部联接。
SELECT C.custid, O.orderid, OD.productid, OD.qty
FROM Sales.Orders AS O
LEFT JOIN Sales.OrderDetails AS OD ON OD.orderid = O.orderid
INNER JOIN Sales.Customers AS C ON O.custid = C.custid

【3】在原有基础上改变,将内部联接变成一个独立的逻辑阶段
USE TSQL2012
GO SELECT C.custid, O.orderid, OD.productid, OD.qty
FROM Sales.Customers AS C
LEFT JOIN
(Sales.Orders AS O INNER JOIN Sales.OrderDetails AS OD ON O.orderid = OD.orderid)
ON C.custid = O.custid
外部联接注意事项 (2)
在外部联接中使用COUNT(*)进行聚合时,它会考虑内部行和外部行,因为它会计算行数而不管它们的内容,如下:
USE TSQL2012
GO SELECT C.custid, COUNT(*) AS numorders
FROM Sales.Customers AS C
LEFT JOIN Sales.Orders AS O ON O.custid = C.custid
GROUP BY C.custid

由之前所查得知客户Id为22的orderid为NULL,即没有订单数量,所以这里就产生了BUG,因为COUNT(*)会包括NULL值,所以这里我们需要替换为COUNT(列名)。
USE TSQL2012
GO SELECT C.custid, COUNT(O.orderid) AS numorders
FROM Sales.Customers AS C
LEFT JOIN Sales.Orders AS O ON O.custid = C.custid
GROUP BY C.custid

总结
本节我们重点讲述了外部联接基本知识以及注意事项,我们下节将讲述联接综合知识,简短的内容,深入的理解,我们下节再会,good night。
SQL Server-外部联接基础回顾(十三)的更多相关文章
- [置顶] 图书推荐:SQL Server 2012 T-SQL基础 Itzik Ben-Gan
经过近三个月的不懈努力,终于翻译完毕了.图书虽然是基础知识,但是,即使你已经使用T-SQL几年,很多地方还是能够弥补你的知识空白.大师级的人物写基础知识,或许你想知道这基础中还有哪些深奥,敬请期待吧. ...
- SQL Server外部链接时报错:Error locating serverInstance specified
SQL Server外部链接时报错:Error locating server/Instance specified 连接时报错信息: 08001 sql server network interfa ...
- SQL Server-交叉联接、内部联接基础回顾(十二)
前言 本节开始我们进入联接学习,关于连接这一块涉及的内容比较多,我们一步一步循序渐进学习,简短内容,深入的理解,Always to review the basics. 交叉联接(CROSS JOIN ...
- 《SQL Server 2012 T-SQL基础》读书笔记 - 5.表表达式
Chapter 5 Table Expressions 一个表表达式(table expression)是一个命名的查询表达式,代表一个有效的关系表.SQL Server包括4种表表达式:派生表(de ...
- SQL Server 存储过程之基础知识(转)
什么是存储过程呢?存储过程就是作为可执行对象存放在数据库中的一个或多个SQL命令. 通俗来讲:存储过程其实就是能完成一定操作的一组SQL语句. 那为什么要用存储过程呢?1.存储过程只在创造时进行编译, ...
- SQL Server数据仓库的基础架构规划
问题 SQL Server数据仓库具有自己的特征和行为属性,有别去其他.从这个意义上说,数据仓库基础架构规划需要与标准SQL Server OLTP数据库系统的规划不同.在本文中,我们将介绍在计划数据 ...
- 《SQL Server 2012 T-SQL基础》读书笔记 - 4.子查询
Chapter 4 Subqueries 子查询分为:独立子查询(Self-Contained Subqueries)和相关子查询(Correlated Subqueries),独立子查询可以单独拿出 ...
- 《SQL Server 2012 T-SQL基础》读书笔记 - 2.单表查询
Chapter 2 Single-Table Queries GROUP BY之后的阶段的操作对象就是组(可以把一组想象成很多行组成的)了,HAVING负责过滤掉一些组.分组后的COUNT(*)表示每 ...
- [译]SQL Server 之 索引基础
SQL Server中,索引以B-tree的结构组织数据.B-tree代表平衡树,但是SQL Server使用一种叫做B+的树. B+树不是总是保持严格的平衡的树. 首先,索引有两个主要的部件:一个页 ...
随机推荐
- Linux平台 Oracle 10gR2(10.2.0.5)RAC安装 Part1:准备工作
Linux平台 Oracle 10gR2(10.2.0.5)RAC安装 Part1:准备工作 环境:OEL 5.7 + Oracle 10.2.0.5 RAC 1.实施前准备工作 1.1 服务器安装操 ...
- 简单粗暴地理解js原型链--js面向对象编程
原型链理解起来有点绕了,网上资料也是很多,每次晚上睡不着的时候总喜欢在网上找点原型链和闭包的文章看,效果极好. 不要纠结于那一堆术语了,那除了让你脑筋拧成麻花,真的不能帮你什么.简单粗暴点看原型链吧, ...
- (系统架构)标准Web系统的架构分层
标准Web系统的架构分层 1.架构体系分层图 在上图中我们描述了Web系统架构中的组成部分.并且给出了每一层常用的技术组件/服务实现.需要注意以下几点: 系统架构是灵活的,根据需求的不同,不一定每一层 ...
- xpath提取多个标签下的text
title: xpath提取多个标签下的text author: 青南 date: 2015-01-17 16:01:07 categories: [Python] tags: [xpath,Pyth ...
- MAVEN学习-第一个Maven项目的构建
MAVEN安装成功之后就可以进行项目的构建和管理了: 为什么要用maven进行项目的构建和管理? 对于初学者来说一个最直接的也是最容易里的优点在于JAR包的管理,相对于以前开发一个项目的时候我们需要用 ...
- JS的内建函数reduce
@(js) reduce函数,是ECMAScript5规范中出现的数组方法.在平时的工作中,相信大家使用的场景并不多,一般而言,可以通过reduce方法实现的逻辑都可以通过forEach方法来变相的实 ...
- C#——传值参数(2)
//我的C#是跟着猛哥(刘铁猛)(算是我的正式老师)<C#语言入门详解>学习的,微信上猛哥也给我讲解了一些不懂得地方,对于我来说简直是一笔巨额财富,难得良师! 这次与大家共同学习C#中的 ...
- Jqprint实现页面打印
好些项目需要实现页面打印,特别是一些后台管理类系统,下面介绍一款轻量级的打印插件: 1.实现页面打印要引入jQuery和Jqprint.点击下载Jqprint插件 <script languag ...
- 【JS基础】循环
for 循环的语法: for (语句 1; 语句 2; 语句 3) { 被执行的代码块 } 语句 1 在循环(代码块)开始前执行 语句 2 定义运行循环(代码块)的条件 语句 3 在循环(代码块)已被 ...
- android Handler介绍
Handler使用介绍: Handler根据接收的消息,处理UI更新.Thread线程发出消息,通知Handler更新UI. Handler mHandler = new Handler() { p ...