【mssql】SQL Server2012编程入门经典(第四版)(上) 读书笔记

时间:2024-08-28 21:36:56

数据库用了很久了,但好多东西很容易忘记,这次头脑发热想起来读一遍书,做点笔记!

从第五章开始参考:《SQL Server 2005 编程入门经典》学习笔记

一、RDBMS基础:SQL Server数据库的构成

1.安装好Sql Server一定包含以下4个系统数据库:

  • Master:此数据库保存一组特殊表(系统表),用于系统的总体控制。如:在服务器新建一个数据库,则会在Master库中sysdatabases表中会记录该信息;任意的存储过程(扩展的或系统的、同一或不同数据库的)都存储在Master数据库中。总而言之,Master数据库存储的信息是描述服务器信息的。不能删除它。
  • model:其他数据库的模板。如果想要修改新建数据库的样式,可以改变model数据库来实现。如:可以向数据库中加入一组审计表或将用户信息复制到每一个新建数据库中。但需要注意:新建数据库的容量要比model容量大。建议不要修改此项。
  • msdb:此数据库是SQL Agent进程存储任意系统任务的地方,如果计划对一数据库每天执行备份计划,则msdb会记录此信息。SSIS程序包和基于策略的管理的定义就是使用msdb的进程实例。
  • tempdb:此数据库是服务器的主要工作区之一。执行一个复杂或大型的操作时,服务器可能需要创建一些中间表来完成,此项工作便是在tempdb库中进行。只要创建临时表、存储临时数据,信息都会保存到tempdb数据库中,但每次关闭服务器时,这些信息都会消亡。tempdb数据库是系统中唯一完全重建的数据库。

2.事务日志:任意数据库的更改起初不会写入数据库本身,而是不断地被写入到事务日志,然后在某个点上,数据库发出检查点,在此时此刻日志中所有更改被写到数据库。

3.用户和角色:用户和角色关系密切。用户(user)相当于登录名,是登录sql server的标识符。登录到sql server的任何人都映射(直接或间接,取决于安全模型)到一个用户。用户一次属于一个或多个角色(Role)。服务器可以赋予用户或角色权限,一个角色可以包含多个用户。

4.数据类型

常见的 int   DateTime   Date   Time   char   Varchar(可变长度字符)   NvarChar(长度可变的Unicode字符)

5.NULL

表示未定义的或者是不能应用的值。不确定的值可设置为NULL。  不一定是0.

NULL值不等于NULL值, NULL的真正含义是“我不知道”,而不是某种意义上的“空”。

二、SQL Server管理工具

       大部分开发人员每天使用的只有SQL Server Management Studio工具。

三、T-SQL基本语句

主要的4条语句 SELECT    INSERT     UPDATE     DELETE  

select语句

(1)、例子:
        SELECT Name , SalesPersonID
        FROM Sales.Store
        WHERE  Name BETWEEN 'g' AND 'j'
                       AND SalesPersonID>283
        ORDER BY SalesPersonID, Name DESC
解释,1、select语句检索——仅仅表示读取数据,后面跟的是列名,若为* 则表示读取所有的列;
            2、from指定要读取的数据所在的一个表或几个表的名称;
            3、where语句过滤——对查找的数据的一个限制。(只要符合条件的几行数据),这里有一些运算符,大于号、小于号、and、or、not、betwwen、通配符like % 、in、all、any;
           4、order by对结果排序(此语句一般在最后),可以按多列,逗号分隔,只要是数据库中的列就行,默认ASC升序,DESC降序。
     5、命名规则: 关键字和命令 全部用大写,而表名、列名、变量名使用大小写混合形式(区分大小写)。(但关键字是不区分大小写的)
(2)、使用group by子句聚合数据——把同一列名下面相同的数据聚在一起。
    聚合函数:(每一个聚合函数结果都产生一个新列)
   1、AVG 平均值
      SELECT SalesPersonID,AVG(orderQty) AS avgorderQty    //将SalesPersonID相同的数据聚在一起,orderQty列
取平均值。as关键字是为了给平均值这一列取个别名
   ------
 GROUP BY SalesPersonID;
   2、Min Max 最小最大值
   3、COUNT(*) 计算查询中返回的行数,不忽略NULL值。。其他的聚合函数均忽略NULL值。
(3)使用HAVING子句给分组设置条件,聚合之后的过滤。
  仅用于有group by子句的查询中,放于group by之后。
(4)distinct 和all 谓词
distinct用在select后面,去除结果中的重复行。
all与之相反,包括所有行(是select语句的默认值),但有union(联合)子句的select语句除外,union结果自动去除重复行。

insert语句

(1)插入一行/几行数据
insert into <table> [(列列表)] values (第一个数据值 eg 'test','1234',---),(第二个数据值)----
省略列列表的话,则是对所有列插入值,而且要对应。
插入数值时不用引号,插入字符数据和date数据时要用引号
(2)插入一块数据 用insert into ----select
insert into 表名1
  select 表名1的列
  from 另一个表/另一个数据库的表
 where ----

update语句 用来更新已有的数据

update 表名
set  列名1='新的值' ,列名2='新的值' 
where ID=4;
SET语句可灵活多变,可为表达式。最好不要更新主键。。

delete语句  删除整行或表

delete 表名
[where 约束条件] 

四、连接join  (操作多个表)

(1)内部连接(排除不匹配的字段)

   仅仅返回那些在两个表中存在字段匹配的记录。(排他特性)类似于where子句
语法结构:SELECT 查询的列名
                FROM 第一个表(左侧)
             [inner] join 第二个表(右侧)
        on  连接条件(表1的某列和表2的某列相等)
  where 第一个表.field='' and ...
说明:inner为默认的,可以不写。。

(2)外部连接 (包含特性)(要匹配数据,返回仅一侧)

left/right [outer]  左连接的结果来自左侧的表,右连接来自右侧的表。

(3)完全连接 (要匹配数据,返回两侧)

full join 返回两侧数据表的所有数据,对方没有匹配的显示null。 此种连接很少用

(4)交叉连接

返回两侧数据表的笛卡尔积,相乘。。使用CROSS关键字,而不使用ON
SELECT v.VendorName, a.AddressName
FROM Vendors v
CROSS JOIN Address a  
(注:v和a分别是表Vendors和 Address 的别名)
(高等数学中有许多笛卡尔积的函数,,交叉连接常用来建立测试数据和科学领域。)

(5)联合(UNION)

用于使两个或两个以上的查询产生一个结果集。 其并不是真正的连接,其作用更像是将一个查询返回的数据附加到另一个查询结果的末尾。。
join将信息水平连接(添加更多列),而union将数据垂直连接(添加更多行)。 UNION 结果集中的列名总是等于 UNION 中第一个 SELECT 语句中的列名。
默认地,UNION 操作符选取不同的值。如果允许重复的值,请使用 UNION ALL。
SELECT column_name(s) FROM table_name1
UNION ALL
SELECT column_name(s) FROM table_name2

五、 创建和修改数据表

一、 SQL Server中的对象名

SQL Server表有4层命名约定。完全限定命名如下所示:

[ServerName.[DatabaseName,[SchemaName.]]]ObjectName

模式名称(SchemaName)

如果使用模式,那么需要指定对象是在哪种模式下的。不同模式下可以有两个同名的对象。如果想访问不在默认模式下的对象,那么需要特别指明对象的模式名称

1. 默认模式:dbo

无论谁创建了数据库,都被认为是"数据库所有者",即dbo。在数据库里面创建的任何对象都带有dbo模式,而不是个体的用户名。

另外,sa(或者sysadmin角色的成员)总是dbo的别名。即无论是谁实际上拥有数据库,sa总拥有完全的权限,就好像是dbo一样。而且sa登录创建的任何对象都显示所有权为dbo。

二、 CREATE语句

CREATE语句用来创建数据库中的对象。CREATE的第一部分看起来总是这样的:

CREATE <object type> <object name>

4.2.1 CREATE DATABASE

CREATE DATABASE <database name>

代码示例:

CREATE DATABASE Accounting

ON

(

NAME = 'Accounting',

FILENAME = 'C:\Program Files\Microsoft SQL Server\MSSQL.1\MSSQL\Data\ AccountingData.mdf',

SIZE = 10MB,

MAXSIZE = 50MB,

FILEGROWTH = 5MB

)

LOG ON

(

NAME = 'AccountingLog',

FILENAME = 'C:\Program Files\Microsoft SQL Server\MSSQL.1\MSSQL\Data\ AccountingLog.ldf',

SIZE = 5MB,

MAXSIZE = 25MB,

FILEGROWTH = 5MB

)

各选项含义:

1. ON

ON用在两个地方:一个定义存储数据的文件的位置,二是定义存储日志的文件的位置。

2. NAME

指定定义的文件的名称,但是只是一个逻辑名称——即SQL
Server在内部使用该名称引用该文件。

3. FILENAME

指定文件的物理名称。数据文件的推荐扩展名为.mdf,日志文件的推荐扩展名为.ldf,附属文件的推荐扩展名为.ndf。

4. SIZE

指定文件的初始大小。默认情况下,大小的单位是MB(兆字节),还可以使用KB、GB或者TB。要记住,这个值至少与模型数据库一样大,而且必须是整数,否则将出错。默认的值与模版数据库一样。

5. MAXSIZE

指定文件长度的最大值。默认情况下,大小的单位是MB。这个选项没有默认值,如果没有提供该选项,则表示不限制最大值。

6. FILEGROWTH

指定当扩张文件时每次的扩张量,可以提供一个值来说明文件每次增加多少字节,或者提供一个百分比,指定文件每次增长的百分比。

7. LOG ON

指定日志文件。注意日志文件默认的大小是数据文件大小的25%。其他方面,日志文件和数据库文件的说明参数相同。

4.2.2 创建数据表

创建表的语法如下:

1. 表和列名称

表和列的推荐命名规则:

  • 名称的每个单词,首字母大写,其他字母小写。
  • 名称尽量短,但是要具有描述性。限制使用缩写,只使用大家都能理解的缩写。例如"ID"表示标识、"No"表示数字、"Org"表示组织。
  • 当名称中有两个单词时,不要用任何分隔符。

2. 数据类型

注意没有默认的数据类型

3. DEFAULT

如果要使用默认值,就必须紧跟在数据类型之后给定这个值。

4. IDENTITY

当你设定一个列为标识列时,SQL Server自动分配一个顺序号给你插入的每个行。注意IDENTITY列和PRIMARY
KEY列是完全不同的概念,既不会因为有一个IDENTITY列就说明这个值是唯一的(例如,可以重新设置种子,使用前面用过的值)。IDENTITY值通常用于PRIMARY
KEY列,但并不是必须这样使用。

6. NULL/NOT NULL

默认的设置是列值是NOT NULL,除非指定允许为空。然而,有很多不同的设置可以改变这个设置,从而影响这个默认值。

7. 列约束

列约束就是对单个列设置的关于该列可插入数据的限制和规则。

9. 表约束

表约束和列约束很相似,但表约束可以基于多个列。表层次的约束包括PRIMARY KEY约束、FOREIGN
KEY约束以及CHECK约束。

12. 创建一个表

USE Accounting

CREATE TABLE Customers

(

CustomerNo INT IDENTITY NOT NULL,

CustomerName VARCHAR(30) NOT NULL,

Address1 VARCHAR(30) NOT NULL,

Address2 VARCHAR(30) NOT NULL,

City VARCHAR(20) NOT NULL,

State CHAR(2) NOT NULL,

Zip VARCHAR(10) NOT NULL,

Contact VARCHAR(25) NOT NULL,

Phone CHAR(15) NOT NULL,

FedIDNo VARCHAR(9) NOT NULL,

DateInSystem SMALLDATETIME NOT NULL

)

使用sp_help存储过程查看表的信息:

EXEC sp_help Customers

4.3 ALTER语句

ALTER语句用来更改对象。ALTER语句总是有相同的开头:

ALTER <object type> <object name>

4.3.1 ALTER DATEBASE

示例:

ALTER DATABASE Accounting

MODIFY FILE

(

NAME = Accounting,

SIZE = 100MB

)

4.3.2 ALTER TABLE

更经常的情况是改变表的结构。这个可以是增加、删除一列或者改变一列的数据类型等。示例:

ALTER TABLE Employees

ADD

PreviousEmployer VARCHAR(30) NULL,

DataOfBirth DATETIME NULL,

LastRaiseDate DATETIME NOT NULL, DEFAULT '2005-01-01'

4.4 DROP语句

DROP语句用来删除对象。

DROP <object type> <object name>[, ...n]

如果需要, 可以同时删除两个表:

USE Accounting

DROP TABLE Customers, Employees

删除整个数据库:

DROP DATABASE Accounting

4.5 使用GUI工具

SQL Server Management Studio  不用写sql语句,图形化操纵数据库。

第5章 键和约束

确保数据的完整性不是使用数据的程序的责任,而是数据库本身的责任。将数据完整性的责任移到数据库本身是数据库管理的一次革命。

较高层次上的3种不同类型的约束:

  • 实体约束
  • 域约束
  • 参照完整性约束

具体的约束类型:

  • PRIMARY KEY约束
  • FOREIGN KEY约束
  • UNIQUE约束(唯一约束)
  • CHECK约束
  • DEFAULT约束

5.1 约束的类型

5.1.1 域约束

域约束处理一个或多个列,确保一个特定列或一组特定列满足特定的标准。

5.1.2 实体约束

实体约束都是关于每个行的。这种形式的约束并不关心一个整体的列,只对特定的行感兴趣,如PRIMARY
KEY约束和UNIQUE约束。

5.1.3 参照完整性约束

参照完整性约束是在某列的值必须与其他列的值匹配时创建的,列可以在同一个表中,或者更通常的是在不同的表中,如FOREIGN
KEY约束。

5.2 约束命名

常见的约束的推荐命名规则如下:

  • CHECK约束以CK开头、主键约束以PK开头、外键约束以FK开头、唯一约束以UN开头。
  • 后接表名、列名。

如在Customers表上对PhoneNo列设置约束:CK_Customers_PhoneNo,Customers表上的主键约束:PK_Custoemrs_CustomerID。

5.3 键约束

常用的键类型:主键、外键、唯一约束。

5.3.1 主键约束

1. 在创建表的时候创建主键约束。主键:每行的唯一标识符,必须包含唯一的值(因此不能为null)。

一个表中最多可以有一个主键。

CREATE TABLE Customers

(

CustomerNo INT IDENTITY NOT NULL PRIMARY KEY,

......

)

2. 在已存在的表上创建主键约束。

USE Accounting

ALTER TABLE Employees

ADD CONSTRAINT PK_EmployeeID

PRIMARY KEY (EmployeeID)

5.3.2 外键约束

外键既能确保数据完整性,也能表现表之间的关系。一个表中可以有多个(0-253)外键,但一个给定的列只能引用一个外键,,一个外键可以涉及多列。

在CREATE语句中设置一列或几列外键约束的语法如下所示:

<column name> <date type> <nullability>

FOREIGN KEY REPERENCES <table name>(<column
name>)

[ON DELETE {CASCADE|NO ACTION|SET NULL|SET DEFAULT}]

[ON UPDATE {CASCADE|NO ACTION|SET NULL|SET DEFAULT}]

示例: (创建了由四列组成的Orders表)

USE Accounting

CREATE TABLE Orders

(

OrderID      INT    IDENTITY    NOT NULL

PRIMARY KEY,

CustomerNo   INT                NOT NULL

FOREIGN
KEY REFERENCES Customers(CustomerNo),

OrderDate  SMALLDATETIME        NOT NULL,

EmpoyeeID    INT                NOT NULL

)

上面的声明:将表Orders的CustomerNo列声明为依赖于外部列(Customers.CustomerNo)

1. 在已存在的表中添加一个外键

ALTER TABLE Orders

ADD CONSTRAINT FK_EmployeeCreatesOrder

FOREIGN KEY (EmployeeID) REFERENCES Employees(EmployeeID)

2. 使一个表自引用

在实际创建自引用约束之前,很关键的一点是在添加外键之前表中至少有一行。

ALTER TABLE Employees

ADD CONSTRAINT FK_EmployeeHasManager

FOREIGN KEY (ManagerEmpID) REFERENCES Employees(EmployeeID)

注:SQL Server不允许删除一个被其他表引用的表(被引用表)。必须先在引用的表中删除外键,才可以删除被引         用表。

3. 级联动作

外键是双向的,即不仅是限制子表的值必须存在于父表中,还在每次对父表操作后检查子行。SQL
Server的默认行为

是在子表存在时"限制"父表不被删除。然而,有时会自动删除任何相关记录,而不是防止删除被引用的记录。同样,

在更新记录时,可能希望相关的记录自动引用刚刚更新的记录。这种进行自动删除和更新的过程称为级联。通过修改

声明外键的语法——添加ON子句,来定义级联操作。

USE Accounting

CREATE TABLE OrderDetails

(

OrderID INT NOT NULL,

PartNo VARCHAR(10) NOT NULL,

Description    VARCHAR(25) NOT NULL,

Qty INT NOT NULL,

CONSTRAINT PK_OrderDetails

PRIMARY KEY (OrderID, PartNo),

CONSTRAINT FK_OrderContainsDetails

FOREIGN KEY (OrderID)

REFERENCES Orders(OrderID)

ON UPDATE NO ACTION

ON DELETE CASCADE

)

如果对外键定义了CASCADE(级联),则操作会从父表级联到子表中。即,如果从父表删除了某项,子表中依赖该项的项都会被删除;如果从父表中更新了某项,则子表中依赖该项的字段也会被更新。

值得注意的是:CASCADE动作所能影响的深度没有限制。

4. 其他操作

NO ACTION为默认操作,即如果子表有依赖,则禁止对父表中的该字段进行删除和更新操作。

SET NULL操作会在父表中的该字段被删除或者更新时,将子表中的依赖项设为NULL,前提是子表中的该项可为NULL值。

SET DEFAULT操作会在父表中的该字段被删除或者更新时,将子表中的依赖项设为在子表中定义的默认值,当然前提是在子表中该字段有默认值。

5.3.3 唯一约束

要求指定的列上有一个唯一值,表中可以有多个唯一约束(而主键只能有一个)

唯一约束不会自动防止您设置一个NULL值。是否允许NULL值取决于表中相应列的NULL选项的设置。然而,要记住如果您确实允许NULL值,那么只能插入一个NULL。

在创建表时设置唯一约束:

CREATE TABLE Shippers

(

ShipperID INT IDENTITY NOT NULL PRIMARY KEY,

ShipperName VARCHAR(30) NOT NULL,

Address VARCHAR(30) NOT NULL,

City VARCHAR(25) NOT NULL,

State CHAR(2) NOT NULL,

Zip VARCHAR(10) NOT NULL,

PhoneNo VARCHAR(14) NOT NULL UNIQUE

)

在已存在的表中创建唯一约束:

ALTER TABLE Employees

ADD CONSTRAINT AK_EmployeeSSN

UNIQUE (SSN)

在约束名称中的AK前缀代表"交替键(Alternate
Key)",也可以使用前缀UQ或者简单的U,代表唯一约束。

5.4 CHECK约束

CHECK约束使用与WHERE字句一样的规则来定义。CHECK约束标准的示例如下:

目标

SQL

限制Month列为合适的数字

BETWEEN 1 AND 12

合适的SSN格式

LIKE '[0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9][0-9][0-9]'

限制Shippers的一个特定列表

IN ('UPS', 'Fed Ex', 'USPS')

价格必须为正

UnitPrice >= 0

在同一行中引用另外一个列

ShipDate >= OrderDate

在已存在的表中添加CHECK约束:

ALTER TABLE Customers

ADD CONSTRAINT CK_CustomerDateInSystem

CHECK (DateInSystem <= GETDATE())

试着插入违反CHECK约束的记录会得到错误。

5.5 DEFAULT约束

DEFAULT约束定义了当插入新行时,在您定义了默认约束的列中没有数据时填充的默认值。要注意:

  • 默认值只在INSERT语句中使用——在UPDATE语句和DELETE语句中被忽略。
  • 如果在INSERT语句中提供了任意的值(包括NULL值),那么就不使用默认值。
  • 如果没有提供值,那么总是使用默认值。

5.5.1 在CREATE TABLE语句中定义DEFAULT约束

示例:

CREATE TABLE Shippers

(

ShipperID INT IDENTITY NOT NULL

PRIMARY KEY,

ShipperName VARCHAR(30) NOT NULL,

DataInSystem SMALLDATETIME NOT NULL

DEFAULT GETDATE()

)

5.5.2 在已存在的表中添加DEFAULT约束

示例:

ALTER TABLE Customers

ADD CONSTRAINT DF_CustomerDefaultDateInSystem

DEFAULT GETDATE()FOR DateInSystem

5.6 使约束失效(禁用约束)

5.6.1 在创建约束时忽略无效的数据

默认情况下,除非已存在的数据满足约束标准,否则SQL Server将不会创建约束。要想在创建约束时,不检查已经在表中的数据是否满足约束,可以在添加约束时添加WITH
NOCHECK选项。示例:

ALTER TABLE Customers

WITH NOCHECK

ADD CONSTRAINT CK_CustomerPhoneNo

CHECK

(Phone LIKE '([0-9][0-9][0-9]) [0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]')

5.6.2 临时使已存在的约束失效

使用NOCHECK选项关闭约束,而不是删除它。示例:

ALTER TABLE Customers

NOCHECK

CONSTRAINT CK_CustomerPhoneNo

当准备重新让约束起效时,使用CHECK选项代替NOCHECK:

ALTER TABLE Customers

CHECK

CONSTRAINT CK_CustomerPhoneNo

第6章 更复杂的查询

6.1 子查询的概念

子查询是嵌套在另外一个查询中的正常的T-SQL查询。在有一个SELECT语句作为部分数据或者另外一个查询的条件

的基础时,通过使用括号创建子查询。

子查询通常用于满足下列需求之一:

  • 将一个查询分隔为一系列的逻辑步骤。
  • 提供一个列表作为WHERE子句或者IN、EXISTS、ANL、SOME、ALL的目标
  • 为父查询中的每个记录提供一个查询表。
注意:大多数的子查询可用连接JOIN来写,且应先考虑连接。

6.2 嵌套的子查询

嵌套的子查询只在一个方向嵌套——返回在外部查询中使用的单个值,或者在IN运算符中使用的一个完整的值列表。

在最松散的意义上说,查询语法看起来像下面的两个语法模板:

SELECT <select list>

FROM <some table>

WHERE <some column> = (

SELECT <single column>

FROM <some table>

WHERE <condition that results in only one row returned>)

或者:

SELECT <select list>

FROM <some table>

WHERE <some column> IN (

SELECT <single column>

FROM <some table>

WHERE <where condition >)

6.2.1 使用单个值的SELECT语句的嵌套查询

例如,假设希望知道每一天通过系统销售的产品的每个条目的ProductID:

SELECT DISTINCK o.OrderDate, od.ProductID

FROM Orders o

INNER JOIN OrderDetails od

ON o.OrderID = od.OrderID

WHERE o.OrderDate = (SELECT MIN(OrderDate) FROM Orders)

红色的是内部查询,内部查询检索的单个的值用于外部查询。因为用的是“=”,所以只能返回一行中的一列。

6.2.2. 使用返回多个值的子查询的嵌套查询

例如,查看所有具有折扣记录的商店列表:

USE Pubs

SELECT stor_id AS "Store ID", stor_name AS "Store Name"

FROM Stores

WHERE stor_id IN (SELECT stor_id FROM Discounts)

注:出于性能方面的考虑,应使用连接方法作为默认解决方案,除非特别理由要使用嵌套的SELECT。

6.2.3. 使用嵌套的SELECT来发现孤立的记录

这种嵌套的SELECT和前面示例几乎相同,区别是添加了NOT运算符。这个不同点时的在转化连接语法时设置等于外部连接而不是内部连接。例如,需要查询所有在Pubs数据库中没有匹配的折扣记录的商店:

SELECT stor_id AS "Store ID", stor_name AS "Store Name"

FROM Stores

WHERE stor_id NOT IN

(SELECT stor_id FROM Discounts WHERE stor_id IS NOT NULL)

6.3 关联的子查询

6.3.1 关联的子查询的工作原理

在相互关联的子查询中,内部查询在外部查询提供的信息上运行,反之亦然。信息传递是双向的。而嵌套查询是单向的,内部查询再外部查询。 关联查询有3个步骤的处理过程:

  • 外部查询获得一个记录,然后将该记录传递到内部查询。
  • 内部查询根据传递的值执行。
  • 内部查询然后将结果值传回到外部查询,外部查询利用这些值完成处理过程。

6.3.2 在WHERE子句中的相互关联的子查询

例如,需要查询系统中每个顾客第一个订单的OrderID和OrderDate:

SELECT o1.CustomerID, o1.OrderID, o1.OrderDate

FROM Orders o1

WHERE o1.OrderDate = (

SELECT MIN(o2.OrderDate)

FROM Orders o2

WHERE
o2.CustomerID = o1.CustomerID)

6.3.3 在SELECT列表中的相互关联的子查询

例如,现在需要查询顾客的姓名和在哪天开始订购商品:

SELECT cu.CompanyName,

(SELECT MIN(OrderDate)

FROM Orders o

WHERE
o.CustomerID = cu.CustomerID) AS "Order Date"

FROM Customers cu

6.3.4 处理NULL数据——ISNULL函数

ISNULL()接受一个变量或者表达式来验证是否是一个空值。如果值确实是NULL,那么函数返回其他预指定的值。如果原来的值不是NULL,那么返回原来的值。语法如下:

ISNULL(<expression to test>, <replacement value if null>)

因此,示例如表所示:

ISNULL表达式

返回值

ISNULL(NULL, 5)

5

ISNULL(5, 15)

5

ISNULL(MyColumnName, 0) where MyColumnName IS NULL

0

ISNULL(MyColumnName, 0) where MyColumnName = 3

3

ISNULL(MyColumnName, 0) where MyColumnName = 'Fred Farmer'

Fred Farmer

使用示例:

SELECT cu.CompanyName,

ISNULL(CAST((SELECT MIN(o.OrderDate)

FROM Orders o

WHERE o.CustomerID = cu.CustomerID) AS VARCHAR), 'NEVER ORDERED')

AS "Order Date"

FROM Customers cu

6.3 派生表

派生表,它是虚表,在数据库中不存在的,是我们构建的,目的是为了缩小数据的查找范围。

例如,现在需要查询既订购了Chocolade又订购了Vegie-spread的所有公司名称。查询代码如下所示:

SELECT DISTINCT c.CompanyName

FROM Customers c

INNER JOIN (

SELECT CustomerID

FROM Orders o

INNER JOIN OrderDetails od

ON o.OrderID = od.OrderID

INNER JOIN Products p

ON od.ProductID = p.ProductID

WHERE p.ProductName = 'Chocolade') AS spen

ON c.CustomerID = spen.CustomerID

INNER JOIN (

SELECT CustomerID

FROM Orders o

INNER JOIN OrderDetails od

ON o.OrderID = od.OrderID

INNER JOIN Products p

ON od.ProductID = p.ProductID

WHERE p.ProductName = 'Vegie-spread') AS spap

ON c.CustomerID = spap.CustomerID

6.4 EXISTS运算符

与IN关键字很相似。

使用EXISTS时,根据是否存在数据满足查询中EXISTS语句所建立的标准,返回一个简单的TRUE和FALSE。并不真正返回数据。例如:

SELECT CustomerID, CompanyName

FROM Customers cu

WHERE EXISTS (

SELECT OrderID

FROM Orders o

WHERE o.CustomerID = cu.CustomerID)

相比连接来说,性能更高。当使用EXISTS关键字时,SQL
Server不需要执行一行一行的连接,而是寻找记录,直到找到第一个匹配的记录,停止在那里。只要有一个匹配,EXISTS就为真,不需要继续查找。

如果需要查询没有订购任何产品的客户,可以使用NOT EXISTS:

SELECT CustomerID, CompanyName

FROM Customers cu

WHERE NOT EXISTS (

SELECT OrderID

FROM Orders o

WHERE o.CustomerID = cu.CustomerID)

6.5 数据类型转换:CAST和CONVERT

CAST和CONVERT都可以执行数据类型转换。在大部分情况下,两者执行相同的功能,不同的是CONVERT还提供一些日期格式转换,而CAST没有这个功能。

注意,CAST是ANSI兼容的,而CONVERT不是。

各自的语法如下:

CAST (expression AS data type)

CONVERT (data type, expression[, style])

CAST和CONVERT可以进行很多数据类型转换,在SQL
Server不进行隐式转换时,需要这种转换。例如:

SELECT 'The Customer has an Order numbered ' + CAST(OrderID AS VARCHAR)

FROM Orders

WHERE CustomerID = 'ALFKI'

例如,需要将timestamp列转换为正常数字。一个timestamp是个二进制数字,因此需要转换:

SELECT CloTS AS "Uncoverted", CAST(ColTS AS INT) AS "Converted"

FROM ConvertTest

还可以转换日期:

SELECT OrderDate, CAST(OrderDate AS VARCHAR) AS "Converted"

FROM Orders

WHERE OrderID = 11050

CONVERT还可以控制日期格式:

SELECT OrderDate, CONVERT(VARCHAR, OrderDate, 111) AS "Converted"

FROM Orders

WHERE OrderID = 11050

CONVERT函数最后一个代码说明需要的格式。注意,任何以超过100表示的是4位的年份;小于100的是两位数字的年份,不过有很少的一些例外,并且小于100表示的格式加上100后即为对应的4位的年份表示的格式。