T-SQL笔记1:SELECT及SELECT高级应用
本章摘要
1:安装AdventureWorks
2:基本运算符和表达式
3:between
4:like
5:escape
6:TOP
7:GROUP BY
7.1:GROUP BY ALL
7.2:HAVING
8:SELECT字句技术
8.1:使用DISTINCT消除重复值
8.2:返回拼接的结果
8.3使用INTO字句
9:子查询
9.1:子查询类型
9.2:代替表达式的查询
9.3:多层嵌套
10:比较使用 EXISTS 和 IN 的查询
11:联接
11.1:使用衍生表
11.2:UNION
12:TABLESAMPLE
13:公共表表达式common_table_expression
1:安装AdventureWorks
本系列笔记均基于AdventureWorks数据库,有关AdventureWorks的安装帮助如下:
在 Management Studio 工具栏上,单击“文件”,指向“打开”,然后单击“文件”。
浏览到文件 instawdb.sql,并单击“打开”。该文件的默认位置为 C:\Program Files\Microsoft SQL Server\90\Tools\Samples\AdventureWorks OLTP。
运行脚本之前,在脚本中找到语句 SET @data_path = @sql_path + 'AWDB\';
,并更改该语句使其指向 instawdb.sql 脚本的位置。例如,SET @data_path = 'C:\Program Files\Microsoft SQL Server\90\Tools\Samples\AdventureWorks OLTP\';
。
执行脚本。
2:基本运算符和表达式
运算符 | 描述 |
!= | 略 |
!> | 略 |
!< | 略 |
< | 略 |
<= | 略 |
<> | 略 |
= | 略 |
> | 略 |
>= | 略 |
ALL | 比较标量值和单列集中的值。 |
ANY | 比较标量值和单列集中的值。SOME 和 ANY 是等效的 |
BETWEEN | 自动根据SQL的型别进行取值 |
CONTAINS | 为单词或短语执行模糊搜索 |
ESCAPE | 指定要以字面值形式搜索,而不是被解释为通配符 |
EXISTS | 指定一个子查询,测试行是否存在 |
FREETEXT | 根据意思,而不是字面值来搜索数据中的单词 |
IN | WHERE color in (‘red’,‘blue’) |
IS NOT NULL | 略 |
IS NULL | 检测NULL值 |
LIKE | 根据通配符进行模式匹配 |
NOT BETWEEN | 略 |
NOT IN | 略 |
NOT LIKE | 略 |
SOME | 比较标量值和单列集中的值。SOME 和 ANY 是等效的 |
3:between
使用:
select SalesOrderID, ShipDate from Sales.SalesOrderHeader
where ShipDate between '7/28/2002' and '7/29/2002'
结果:将会返回17条语句。
也许有人会用,使用:
where'7/28/2002' < ShipDate and ShipDate < '7/29/2002'
不行吗?答案是不行。结果会返回0。
4:like
存在如下通配符,
%:0~N个任意字符;
_:1个字符;
[]:指定范围或列表中的任何单个字符;
[^]:指定不再范围中的任何单个字符;
5:escape
where name like ‘b/B%’ escape ‘/’
解析:表示全部以‘b/B’开头的name,其中/不理解为通配符。
6:TOP
top允许根据定义的行的数量或者百分比查询出开始的N行。如:
select top 10 from …
或者:
declare @percentage float
set @percentage =1
select top (@percentage) percent * from Sales.SalesOrderHeader
7:GROUP BY
指定用来放置输出行的组。如果 SELECT 子句 <select list> 中包含聚合函数,则 GROUP BY 将计算每组的汇总值。
上面这句话不太好理解,更好的理解应该解释为:
“由于在SELECT字句中使用了聚合函数,未聚合的列必须出现在GROUP BY子句中。”
select OrderDate, sum(totalDue) TotalDueByOrderDate from Sales.SalesOrderHeader
where ShipDate between '7/28/2002' and '7/29/2002'
group by OrderDate
结果:
(2 行受影响)
7.1:GROUP BY ALL
在上面的代码中,加入ALL,即:
select OrderDate, sum(totalDue) TotalDueByOrderDate from Sales.SalesOrderHeader
where ShipDate between '7/28/2002' and '7/29/2002'
--group by OrderDate
group by all OrderDate
结果:
Warning: Null value is eliminated by an aggregate or other SET operation.
(1124 行受影响)
这说明:ALL包含所有组和结果集,甚至包含那些其中任何行都不满足 WHERE 子句指定的搜索条件的组和结果集。如果指定了 ALL,将对组中不满足搜索条件的汇总列返回空值。
7.2:HAVING
指定组或聚合的搜索条件。HAVING 只能与 SELECT 语句一起使用。HAVING 通常在 GROUP BY 子句中使用。如果不使用 GROUP BY 子句,则 HAVING 的行为与 WHERE 子句一样。
相当于对GROUP之前的查询内容进行再一次的条件检索。
以下示例使用简单 HAVING
子句从 SalesOrderDetail
表中检索超过 $100000.00
的每个 SalesOrderID
的总计。
SELECT SalesOrderID, SUM(LineTotal) AS SubTotal
FROM Sales.SalesOrderDetail
--where ModifiedDate between '7/28/2002' and '7/29/2002'
GROUP BY SalesOrderID
HAVING SUM(LineTotal) > 100000.00
--HAVING SalesOrderID = 43875
--HAVING ModifiedDate between '7/28/2002' and '7/29/2002' --error
ORDER BY SalesOrderID ;
8:SELECT字句技术
SELECT字句技术有很多,除了最简单的拼接等,下面介绍个人认为最有用的。
8.1:使用DISTINCT消除重复值
select * FROM HumanResources.Employee
结果:(290 行受影响)
select DISTINCT HireDate FROM HumanResources.Employee
结果:(164 行受影响)
说明已经将重复的抵消了。
8.2:返回拼接的结果
DECLARE @Shifts varchar(20)
SET @Shifts = ''
SELECT @Shifts = @Shifts + s.Name + ',' FROM HumanResources.Shift s
SELECT @Shifts
返回的结果为:Day,Evening,Night,
这对于我们处理简单的查询并提高效率有很大的好处。
8.3使用INTO字句
INTO字句用来创建新表(对我来说便是备份数据)。
一种典型的用法是复制数据到新表(这个新表可以被创建为永久表、临时表或全局临时表),如下代码:
SELECT CustomerID, Name, SalesPersonID, Demographics
INTO Store_Archive
FROM Sales.Store
结果:(701 行受影响)
说明,1:创建了新表Store_Archive,2:有701行数据被复制到了Store_Archive。
当然,如果你仅仅想创建新表,而不想复制任何数据,有一个简洁的方法是:
SELECT CustomerID, Name, SalesPersonID, Demographics
INTO Store_Archive
FROM Sales.Store
WHERE 1=0
9:子查询
子查询是一个嵌套在 SELECT、INSERT、UPDATE 或 DELETE 语句或其他子查询中的查询。任何允许使用表达式的地方都可以使用子查询。
联接总是可以表示为子查询。子查询经常(但不总是)可以表示为联接。这是因为联接是对称的:无论以何种顺序联接表 A 和 B,都将得到相同的结果。而对子查询来说,情况则并非如此。
使用联接而不使用子查询处理该问题及类似问题的一个不同之处在于,联接使您可以在结果中显示多个表中的列。例如,如果要在结果中包括产品子类别的名称,则必须使用联接版本。
9.1:子查询类型
可以在许多位置指定子查询(必须全部掌握):
- 使用别名。有关详细信息,请参阅使用别名的子查询。
- 使用 IN 或 NOT IN。有关详细信息,请参阅使用 IN 的子查询和使用 NOT IN 的子查询。
- 在 UPDATE、DELETE 和 INSERT 语句中。有关详细信息,请参阅 UPDATE、DELETE 和 INSERT 语句中的子查询。
- 使用比较运算符。有关详细信息,请参阅使用比较运算符的子查询。
- 使用 ANY、SOME 或 ALL。有关详细信息,请参阅用 ANY、SOME 或 ALL 修改的比较运算符。
- 使用 EXISTS 或 NOT EXISTS。有关详细信息,请参阅使用 EXISTS 的子查询和使用 NOT EXISTS 的子查询。
- 代替表达式。有关详细信息,请参阅用于替代表达式的子查询。
9.2:代替表达式的查询
必须着重说说代替表达式的子查询。在 Transact-SQL 中,除了在 ORDER BY 列表中以外,在 SELECT、UPDATE、INSERT 和 DELETE 语句中任何能够使用表达式的地方都可以用子查询替代。
以下示例说明如何使用此增强功能。此查询找出所有山地车产品的价格、平均价格以及两者之间的差价。
USE AdventureWorks;
GO
SELECT Name, ListPrice,
(SELECT AVG(ListPrice) FROM Production.Product) AS Average,
ListPrice - (SELECT AVG(ListPrice) FROM Production.Product)
AS Difference
FROM Production.Product
WHERE ProductSubcategoryID = 1
9.3:多层嵌套
子查询自身可以包括一个或多个子查询。一个语句中可以嵌套任意数量的子查询。
以下查询将查找作为销售人员的雇员的姓名。
Use AdventureWorks;
GO
SELECT LastName, FirstName
FROM Person.Contact
WHERE ContactID IN
(SELECT ContactID
FROM HumanResources.Employee
WHERE EmployeeID IN
(SELECT SalesPersonID
FROM Sales.SalesPerson)
10:比较使用 EXISTS 和 IN 的查询
以下示例比较了两个语义等同的查询。第一个查询使用 EXISTS
,第二个查询使用 IN
。
USE AdventureWorks ;
GO
SELECT a.FirstName, a.LastName
FROM Person.Contact AS a
WHERE EXISTS
(SELECT *
FROM HumanResources.Employee AS b
WHERE a.ContactId = b.ContactID
AND a.LastName = 'Johnson');
GO
下面的查询使用 IN
。
USE AdventureWorks ;
GO
SELECT a.FirstName, a.LastName
FROM Person.Contact AS a
WHERE a.LastName IN
(SELECT a.LastName
FROM HumanResources.Employee AS b
WHERE a.ContactId = b.ContactID
AND a.LastName = 'Johnson');
GO
以下是其中任一查询的结果集。
FirstName LastName
-------------------------------------------------- ----------
Barry Johnson
David Johnson
Willis Johnson
(3 row(s) affected)
11:联接
通过联接,可以从两个或多个表中根据各个表之间的逻辑关系来检索数据。
联接条件中用到的列不必具有相同的名称或相同的数据类型。但如果数据类型不相同,则必须兼容,或者是可由 SQL Server 进行隐式转换的类型。
联接可分为以下几类:
- 内部联接(典型的联接运算,使用类似于 = 或 <> 的比较运算符)。内部联接包括同等联接和自然联接。
内部联接使用比较运算符根据每个表的通用列中的值匹配两个表中的行。例如,检索 students 和 courses 表中学生标识号相同的所有行。 - 外部联接。外部联接可以是左向外部联接、右向外部联接或完整外部联接。
在 FROM 子句中可以用下列某一组关键字来指定外部联接:- LEFT JOIN 或 LEFT OUTER JOIN。
左向外部联接的结果集包括 LEFT OUTER 子句中指定的左表的所有行,而不仅仅是联接列所匹配的行。如果左表的某一行在右表中没有匹配行,则在关联的结果集行中,来自右表的所有选择列表列均为空值。 - RIGHT JOIN 或 RIGHT OUTER JOIN
右向外部联接是左向外部联接的反向联接。将返回右表的所有行。如果右表的某一行在左表中没有匹配行,则将为左表返回空值。 - FULL JOIN 或 FULL OUTER JOIN
完整外部联接将返回左表和右表中的所有行。当某一行在另一个表中没有匹配行时,另一个表的选择列表列将包含空值。如果表之间有匹配行,则整个结果集行包含基表的数据值。
- LEFT JOIN 或 LEFT OUTER JOIN。
- 交叉联接
交叉联接将返回左表中的所有行。左表中的每一行均与右表中的所有行组合。交叉联接也称作笛卡尔积。
11.1:使用衍生表
衍生表是指在FROM字句中作为表的SELECT语句。
SELECT DISTINCT s.PurchaseOrderNumber
FROM Sales.SalesOrderHeader s
INNER JOIN ( SELECT SalesOrderID
FROM Sales.SalesOrderDetail
WHERE UnitPrice BETWEEN 1000 AND 2000) d ON
s.SalesOrderID = d.SalesOrderID
11.2:UNION
将两个或更多查询的结果合并为单个结果集,该结果集包含联合查询中的所有查询的全部行。
下面列出了使用 UNION 合并两个查询结果集的基本规则:
- 所有查询中的列数和列的顺序必须相同。
- 数据类型必须兼容。
SELECT ProductModelID, Name FROM Production.ProductModel WHERE ProductModelID NOT IN (3, 4) UNION SELECT ProductModelID, Name FROM dbo.Gloves ORDER BY Name ;
12:TABLESAMPLE
TABLESAMPLE 子句将从 FROM 子句中的表返回的行数限制到样本数或行数的某一百分比。例如:
SELECT FirstName, LastName FROM Person.Person TABLESAMPLE (10 PERCENT) ;
SELECT FirstName, LastName FROM Person.Person TABLESAMPLE (100 ROWS) ;
13:公共表表达式common_table_expression
指定临时命名的结果集,这些结果集称为公用表表达式 (CTE)。该表达式源自简单查询,并且在单条 SELECT、INSERT、UPDATE 或 DELETE 语句的执行范围内定义。该子句也可用在 CREATE VIEW 语句中,作为该语句的 SELECT 定义语句的一部分。公用表表达式可以包括对自身的引用。这种表达式称为递归公用表表达式。
USE AdventureWorks;
GO
WITH DirReps(ManagerID, DirectReports) AS
(
SELECT ManagerID, COUNT(*)
FROM HumanResources.Employee AS e
WHERE ManagerID IS NOT NULL
GROUP BY ManagerID
)
SELECT ManagerID, DirectReports
FROM DirReps
ORDER BY ManagerID;
GO