续 2008技术内幕:T-SQL语言基础 单表查询摘记

第三章 联接查询

  Microsoft SQL Server 2008 支持四种表运算符 join(ANSI标准)、apply(T-SQL扩展)、pivot(T-SQL扩展)、unpivot(T-SQL扩展)。

  (ANSI SQL是美国国家标准学会(ANSI) 对SQL进行规范化后的国际标准SQL语言,T-SQL是Microsoft SQL Server基于ANSI SQL做了一些扩展形成的专用SQL语言。)

  join表运算符对两个输入表进行操作,有三种基本类型:交叉联接内联接外联接

  处理步骤

      交叉联接只有一个步骤--笛卡尔积

      内联接有两个步骤--笛卡尔积过滤

      外联接有三个步骤--笛卡尔积过滤添加外部行

  出于优化的目的,SQL Server关系引擎经常会采用很多处理捷径(数据库引擎并没有严格按照逻辑查询处理步骤),因为他知道这样的处理仍然能够生成正确的结果。

  交叉联接

  SQL Server支持交叉联接的两种标准语法--ANSI SQL-92 和 ANSI SQL-89 推荐用ANSI SQL-92(下面有推荐说明)。

--ANSI SQL-92标准 CROSS JOIN 关键字
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

  这两种语法在逻辑上和性能上都没有区别,Customers表有91行而Employees有9行数据,所以上面的查询会生成一个819行数据的结果集。

  笛卡尔积:如果一个表有m行,而另个表有n行,将得到m*n行的结果集。

  图11

  自交叉联接:顾名思义一个表实现自己连接自己。交叉联接、内链接、外连接都支持自联接。上面笛卡尔积图就是一个自交叉联接实例。

SELECT  E1.empid ,E1.firstname ,E1.lastname ,
E2.empid ,E2.firstname ,E2.lastname
FROM hr.Employees AS E1
CROSS JOIN hr.Employees AS E2

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

--利用自联接生成数字表
DECLARE @table TABLE
(
digit INT NOT NULL PRIMARY KEY
)
INSERT INTO @table( digit )VALUES ( 0 ),( 1 ),( 2 ),( 3 ),( 4 ),( 5 ),( 6 ),( 7 ),( 8 ),( 9 ) SELECT T1.digit + T2.digit * 10 + T3.digit * 100 + 1 AS d
FROM @table AS T1
CROSS JOIN @table AS T2
CROSS JOIN @table AS T3
ORDER BY d

  内联接
    两个逻辑步骤:一,对两个输入的表进行笛卡尔积运算,二,过滤。

   --ANSI SQL-92标准
    两表之间必须指定inner join关键字,inner关键字是可选的,因为内联接是默认的联接方式,可以单独指定join关键字。

SELECT  e.empid ,e.firstname ,e.lastname ,o.orderid
FROM hr.Employees AS e
JOIN Sales.Orders AS o ON e.empid = o.empid

    on条件子句和where、having子句一样,只返回结果为true的行,而不会返回谓词计算结果为false、unknow的行

--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-92标准的联接语法,因为在某些方面它用起来更安全。如果你写一条内联接查询但忘记了指定联接条件,如果这时候用的是ANSI SQL-92标准,查询语句将是无效的,语法分析器会报错。即使错误提示信息没有马上指出错误是缺少了联接条件,但最终还是可以想办法找到问题在哪,修改好查询语句。

    而ANSI SQL-89语法,如果忘记了指定联接条件,查询仍然是有效的,执行的结果集却是一个交叉查询

  

   错误例子

SELECT  e.empid ,e.firstname ,e.lastname ,o.orderid
FROM hr.Employees AS eJOIN
JOIN Sales.Orders AS o 错误:Incorrect syntax near 'o'.

  如果使用的是ANSI SQL-89标准语法,忘记了写连接条件,

SELECT  e.empid ,e.firstname ,e.lastname ,o.orderid
FROM hr.Employees AS e ,
Sales.Orders AS o

  那么恭喜得了一个大数据的大奖!爽歪歪!逻辑一复杂,错误在哪里也不知。

  坚持推荐使用ANSI-92的原因:

  1.是保持一致性(都有关键字)

  2.减少错误(以防逻辑条件被遗漏)

  3.提高维护性(一个写好的存储过程,你也不知道会被后面的人改几次,本来是个交叉联接改来改去成了内联接!这个是你无法预算的,但是这是可避免,直接标明cross join.)

  组合联接:就是联接条件涉及两边多个列的查询

SELECT  *
FROM tabName1 AS A
INNER JOIN tabName2 AS B ON A.id1 = B.id
AND A.id = B.id2

  不等联接:如果联接条件只包含等号运算符,那么这样的联接叫做等值联接,如果联接条件包含除等号以外的其他运算符,那么这样的联接叫做不等联接。

SELECT  *
FROM tabName1 AS A
INNER JOIN tabName2 AS B ON A.id1 > B.id

  多表联接

    一个联接表运算符只对两个表进行操作,而一条查询语句可以包含多个联接。

    通常,当From子句中包含多个表运算符时,表运算符在逻辑上是按从左到右的顺序处理的。

 SELECT c.custid ,c.companyname ,o.orderid ,od.productid ,od.qty
FROM Sales.Customers AS c
INNER JOIN Sales.Orders AS o ON c.custid = o.custid
INNER JOIN Sales.OrderDetails AS od ON o.orderid = od.orderid

  外联接

    外联接是在ANSI SQL-92中才被引入的,因此它只有一种标准语法。逻辑步骤:笛卡尔积、on过滤、添加外部行。

  在外联接中,要把一个表标记为“保留的”表,可以在表名之间使用关键字 left outer join、right outer join、full outer join,其中outer关键字是可选的(left join 、right join、full join )。

  left关键字表示左边的表的行是保留的,right关键字表示右边表的行是保留的,full关键字则表示左右两边表的行都是保留的。对于来自联接的非保留表的那些列,追加的外部行中的这些列则用null作为占位符。

SELECT  C.custid ,C.companyname ,O.orderid
FROM Sales.Customers AS C
LEFT JOIN Sales.Orders AS O ON C.custid = O.custid

  题目:查询订单表中所有订单,查出从2007年1月1日到2010年12月12日之间的每个日期输订单个数。

DECLARE @table TABLE
(
[num] INT NOT NULL PRIMARY KEY ,
[date] DATETIME NOT NULL
)
SET NOCOUNT ON
DECLARE @i INT= 1
BEGIN TRAN
WHILE ( @i <= 3000 )
BEGIN
INSERT INTO @table
( num, date )
VALUES ( @i, -- num - int
DATEADD(DAY, @i - 1, '') -- date - datetime
) ;
SET @i = @i + 1 ;
END
COMMIT TRAN
SET NOCOUNT OFF SELECT CAST(d.date AS DATE) AS [date] ,
COUNT(o.orderid) AS [count]
FROM @table AS D
LEFT JOIN Sales.Orders AS O ON CAST(d.date AS DATE) = CAST(o.orderdate AS DATE)
WHERE CAST(d.date AS DATE) BETWEEN ''
AND ''
GROUP BY CAST(d.date AS DATE)

  思路:

    --1 建立一个辅助表,往里面添加数据,从2006年1月1日到2014年03月17日
    --2 利用外联接查询

  ISNULL()函数

  语法

  isnull(check_expression,replacement_value) returns int

SELECT  [date] ,ISNULL(orderid, 0) AS 'default' ,orderid
FROM @table AS D
LEFT JOIN Sales.Orders AS O ON CAST(d.date AS DATE) = CAST(o.orderdate AS DATE)

  图

  外联接的条件过滤

  对外联接表中非保留表的列值进行过滤,这通常就是存在错误的一个标志。因为外联接得到的外部行中的值有可能是null,而null<运算符><值>运算,得到的只会是Unknow。

  例子

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

  上面这条查询语句是对Customers表和Orders表执行左联接操作,Customers作为保留表,在where条件筛选之前,o.orderdate是有两条为null的值使用O.orderdate >= '20070101'过滤条件后,直接将这两条剔除了,因为null和任何比较都是unknow,而where只支持逻辑值为true的表达式,这样的查询条件会让所有外部行都被过滤掉,效果上相当于抵消了外联接的作用,换句话说,这里就是把外联接当成内联接用了。

  练习
    1-1.执行以下代码

SET NOCOUNT ON;
USE TSQLFundamentals2008;
IF OBJECT_ID('dbo.Nums','U') IS NOT NULL
DROP TABLE dbo.Nums;
CREATE TABLE dbo.Nums(n INT NOT NULL
PRIMARY KEY) DECLARE @i INT = 1 ;
BEGIN TRAN
WHILE ( @i <= 100000 )
BEGIN
INSERT INTO Nums
( n )
VALUES ( @i -- n - int
)
SET @i = @i + 1 ;
END
COMMIT TRAN
SET NOCOUNT OFF

  1-2 写一条查询语句,把所有雇员记录复制5次。

SELECT  empid ,firstname ,lastname ,n
FROM HR.Employees AS E
CROSS JOIN dbo.Nums AS T
WHERE n < = 5

  1-3 写一个查询,为每个雇员从2009年06月12日致2009年6月12日范围内的每天返回一行。

SELECT  H.empid ,
DATEADD(DAY, n - 1, '') AS [date]
FROM dbo.Nums AS N
CROSS JOIN HR.Employees AS H
WHERE DATEADD(DAY, n - 1, '') BETWEEN ''
AND ''
ORDER BY h.empid

  2.返回来自美国的客户,并为每个客户返回其订单总数和商品交易总数量。

SELECT  C.custid ,
COUNT(DISTINCT O.orderid)AS [count] ,
SUM(OD.qty) AS totalqty
FROM Sales.Customers AS C
INNER JOIN Sales.Orders AS O ON C.custid = O.custid
INNER JOIN Sales.OrderDetails AS OD ON O.orderid = OD.orderid
WHERE C.country = 'USA'
GROUP BY C.custid

  3.返回客户及其订单信息,包括没有下过任何订单的客户.

SELECT  c.custid ,c.companyname ,o.orderid ,o.orderdate
FROM Sales.Customers AS C
LEFT JOIN Sales.Orders AS O ON C.custid = O.custid

  4.返回在2007年02月12日下过订单的客户,以及他们的订单,同时也返回在2007年2月12日没有下过订单的客户.

SELECT  c.custid ,c.companyname ,o.orderid ,o.orderdate
FROM Sales.Customers AS C
LEFT JOIN ( SELECT custid ,orderid ,orderdate
FROM Sales.Orders
WHERE CAST(orderdate AS DATE) = ''
) AS O ON C.custid = O.custid

  5.返回所有客户信息,并根据客户是否在2007年2月12日日下过订单,再为每个客户返回一列 Yes/No值。上一题的扩展。用case when

SELECT  c.custid ,c.companyname ,
CASE WHEN o.orderid IS NULL THEN 'No'
ELSE 'Yes'
END HasOrderOn20070212
FROM Sales.Customers AS C
LEFT JOIN ( SELECT custid ,
orderid ,
orderdate
FROM Sales.Orders
WHERE CAST(orderdate AS DATE) = ''
) AS O ON C.custid = O.custid

2008技术内幕:T-SQL语言基础 联接查询摘记的更多相关文章

  1. 2008技术内幕:T-SQL语言基础

    2008技术内幕:T-SQL语言基础 单表查询摘记 这里的摘抄来自<Microsoft SQL Server 2008技术内幕:T-SQL语言基础>,书中用到的案例数据库是这个 TSQLF ...

  2. 2008技术内幕:T-SQL语言基础 单表查询摘记

    这里的摘抄来自<Microsoft SQL Server 2008技术内幕:T-SQL语言基础>,书中用到的案例数据库是这个 TSQLFundamentals2008 ,官网给出的连接是这 ...

  3. 学习Microsoft SQL Server 2008技术内幕:T-SQL语法基础

    第 2 章: 单表查询 use TSQLFundamentals2008; select * from Sales.orders; select empid, year(orderdate) as o ...

  4. 学习Microsoft SQL Server 2008技术内幕:T-SQL语法基础--第4章

    第4章 子查询 4.2.1 Exist 谓语: use TSQLFundamentals2008 select * from Sales.Customers as C where c.country= ...

  5. Microsoft SqlServer2008技术内幕:T-Sql语言基础-读书笔记-单表查询SELECT语句元素

    1.select语句逻辑处理顺序: FORM WHERE GROUP BY HAVING SELECT OVER DISTINCT TOP ORDER BY 总结: 2.FORM子句的表名称应该带上数 ...

  6. Microsoft SqlServer2008技术内幕:T-Sql语言基础-读书笔记1

    一.理论背景:关系模型,其数学理论是集合论和谓词逻辑. 1.集合论:集合定义是把我们直观或思维中确定的,相互间有明确区别的那些对象视为一个整体,这个整体就是集合. 2.谓词逻辑:谓词是判断对象是否有某 ...

  7. 《[MySQL技术内幕:SQL编程》读书笔记

    <[MySQL技术内幕:SQL编程>读书笔记 2019年3月31日23:12:11 严禁转载!!! <MySQL技术内幕:SQL编程>这本书是我比较喜欢的一位国内作者姜承尧, ...

  8. SQLServer 2008 技术内幕——T-SQL 查询 笔记

    原文:SQLServer 2008 技术内幕--T-SQL 查询 笔记 1.SQL编程有许多独特之处,如:面向集合的思维方式.查询元素的逻辑处理顺序.三值逻辑.如果不掌握这些知识就开始用SQL编程,得 ...

  9. SQL语言基础和数据库操作

    Sql语言基础: 核心思想:我们自己构造一段查询的代码,然后添加到语句后,从而得到想要的某些数据. Mysql是一种开源数据库 APP Serv:Apache+php+mysql,相当于phpstud ...

随机推荐

  1. UBB/HTML互相转换简单实现源码一览

    查看源码,主要用的就是正则匹配,多的不说,直接读码. 资源原地址:在线UBB/HTML转换 效果图如下 以下源码: <!DOCTYPE html> <html lang=" ...

  2. Noip模拟题 Matrix [递推,组合数]

    Matrix 时间限制: 1 Sec  内存限制: 512 MB 题目描述 小 z 的女朋友送给小 z 一个 n × n 的矩阵.但是矩阵实在太大了,小 z 的女朋友拿不动,只能带给他两个长度为 n ...

  3. Mysql自增语句

    一.创建查询 二.将 alter table `表名` change id id int not null auto_increment UNIQUE;复制进去(以id为例) 三.运行ok 注意:手动 ...

  4. poi类包对比

  5. JAVAEE——宜立方商城13:订单系统实现、订单生成、Mycat数据库分片

    1. 学习计划 1.订单系统实现 2.订单生成 3.Mycat数据库分片 2. 订单系统 2.1. 功能分析 1.在购物车页面点击“去结算”按钮,跳转到订单确认页面 a) 必须要求用户登录 b) 使用 ...

  6. 使用Mongo索引需要注意的几个点

    1.正则表达式和取反运算符不适合建立索引 正则表达式:$regex 取反运算符:$ne ,$nin 2.backgroud建立索引速度缓慢 前台创建是会有阻塞,backgroud效率缓慢,实际情况实际 ...

  7. 【基础知识】C#数据库中主键类型的选择

    主键在数据库中占有很大的地位,对于表的关联性,和数据的唯一识别性有重要的作用: 1,在C#开发中,Int自增字段和Guid(数据库中是uniqueidentifier类型)可设置为主键: 1>G ...

  8. 面向对象设计原则 迪米特法则(Law of Demeter)

    迪米特法则(Law of Demeter) 又叫作最少知识原则(Least Knowledge Principle 简写LKP),英文简写为: LoD. 这是一种面向对象程序设计的指导原则,它描述了一 ...

  9. Spring的模块组成

    Spring的模块组成 1.核心容器:核心容器提供 Spring 框架的基本功能(Spring Core).核心容器的主要组件是 BeanFactory,它是工厂模式的实现. BeanFactory ...

  10. luoguP1659 [国际集训队]拉拉队排练 manacher算法

    直接统计长度为$i$的回文子串有多少个 然后倒叙枚举长度,快速幂统计一下即可 复杂度$O(n \log n)$ #include <cstdio> #include <cstring ...