数据库三级
Posted 364.99°
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据库三级相关的知识,希望对你有一定的参考价值。
目录
6.高级数据查询
选择、填空
6.1.一般数据查询功能扩展
6.1.1.SELECT查询语句格式
SELECT语句是SQL语言中功能强大、使用灵活的语句之一,它能够实现数据的选择、投影和连接运算,并且能够完成筛选字段的重命名、分类汇总排序和多数据源组合等具体操作
SELECT语句一般格式
SELECT [ALL|DISTINCT|TOP n] # |<字段列表>[,<表达式>AS<标识符>]
FROM<表名1>[,<表名2>]...
[WHERE <条件表达式>]
[GROUP BY <字段名>[HAVING<条件表达式>]]
[ORDER BY <字段名> [ASC|DESC]]
该语句从指定的基本表中,创建一个由指定范围内、 满足条件、按某字段分组、按某字段排序的指定字段你组成的新纪录集
6.1.2.使用TOP限制结果集
当使用SELECT语句进行查询时,有时只希望列出结果集中的前几行数据,而不是全部数据;可以使用TOP谓词来限制输出的结果
TOP谓词的语法格式:
TOP n [ percent ] [ WITH TIES] #其中,n为非负整数
参数含义:
- TOP n :取查询结果的前n行数据
- TOP n percent :取查询结果的前n%行数据
- WITH TIES :表示包括最后一 行取值并列的结果
注意: TOP谓词写在SELECT单词的后边(如果有DISTINCT的话,则TOP写在DISTINCT的后边)、查询列表的前边
案例:设有图书出版表(书号,书名,出版日期,单价) , 其中“书号"是主键。现要查询2019年出版的单价最高的图书的书名和单价
SELECT 书名单价 FROM 图书出版表
WHERE 书号 in (
SELECT TOP 1 WITH TIES 书号 FROM 图书出版表 WHERE year(出版日期) = 2019
ORDER BY 单价 desc ) #desc降序
注意:在使用TOP谓词时,应该与ORDER BY子句一起使用,这样的前几名才有意义。当使用WITH TIES选项时,则要求必须使用ORDER BY子句
6.1.3.使用CASE函数
可以在查询语句中使用CASE函数,以达到分情况显示不同类型的数据的目的。CASE函数是一种多分支表达式, 它计算条件列表并返回多个可能的结果表达式中的一个。
- 简单CASE函数
CASE 测试表达式
WHEN 简单表达式1 THEN 结果表达式1
WHEN 简单表达式2 THEN 结果表达式2
...
WHEN 简单表达式n THEN 结果表达式n
[ELSE 结果表达式 n+1]
END
执行过程
- 计算测试表达式,然后按从上到下的顺对每个WHEN子句的简单表达式进行计算
- 如果某个简单表达式的值与测试表达式的值相匹配,则返回与第一个取值为True的WHEN对应的结果表达式的值
- 如果所有的简单表达式的值都不与测试表达式的值相匹配,则当指定了ELSE子句时,将返回ELSE子句中指定的结果表达式的值;若没有指定ELSE子句,则返回NULL值
- 搜索CASE函数
CASE
WHEN 布尔表达式1 THEN 结果表达式1
WHEN 布尔表达式2 THEN 结果表达式2
...
WHEN 布尔表达式n THEN 结果表达式n
[ELSE 结果表达式 n+1]
END
搜索CASE函数的各个WHEN子句的布尔表达式可以使用比较运算符,也可以使用逻辑运算符。搜索CASE函数的执行过程同简单CASE函数。
案例:设在SQL Server 2008某数据库中有表SC (Sno, Cno,Grade), 其中Grade列的类型为int。若在查询成绩时,希望将成绩按"优"、 “良”、 “中”、“及格" 和 “不及格” 形式显示
CASE
WHEN Grade between 90 and 100 THEN '优'
WHEN Grade between 80 and 89 THEN '良'
WHEN Grade between 70 and 79 THEN '中'
WHEN Grade between 60 and 69 THEN '及格'
ELSE '不及格'
END
6.1.4.将查询结果保存到新表中
当使用SELECT语句查询数据时,产生的结果是保存在内存中的。若将查询结果永久保存下来,比如保存在一个表中,可以通过在SELECT语句中使用INTO子句实现。
包含INTO子句的语法格式:
SELECT 查询列表序列 INTO <新表名>
FROM 数据源
... #其他行过滤、分组等子句
其中“新表名"是存放查询结果的表名。这个语句将查询的结果保存在"新表名"所指定的表中
此语句的功能:
- 根据查询语句列出的列以及其类型创建一个新表
- 将查询的结果插人到新表中
- 行查询语句
用INTO子句创建的新表可以是永久表(在执行这个语句时所使用的数据库中被物理的创建,并且是存储在磁盘上的表),也可以是临时表(在tempdb数据库中创建此表,其生存期是有限的)。
临时表又根据其使用范围分为局部临时表和全局临时表:
- 全局临时表
1.1 通过在表名前加两个"#"来标识,比如: ##T1,表示 “##T1” 是一个全局临时表
1.2 全局临时表的生存期与创建全局临时表的用户的连接生存期相同,并且在生存期内可以被所有的连接使用- 局部临时表
2.1 通过在表名前加一一个 “#” 来标识,比如:#T1,表示 “#T1” 是一个局部临时表
2.2 局部临时表的生存期与创建此局部临时表的用户的连接生存期相同,它只能在创建此局部临时表的当前连接中使用
案例:查询购买了 “家用电器” 类商品的顾客姓名、所在地址、购买日期、购买数量和购买单价,并将查询结果保存到一个新的永久表中,新表名为 "家用电器销售表”,新表中各列名用中文
SELECT CName 顾客名,Address 地址,SaleDate 购买日期,Quantity 数量,UnitPrice 购买价格
INTO 家用电器销售表
FROM Table_Customer a JOIN Table SaleBill b ON a.CardID = b.CardID
JOIN Table_SaleBillDetail c ON c.SaleBilID=b.SaleBilID
JOIN Table_Goods d ON d.GoodsID=c.GoodsID
JOIN Table_GoodsClass e ON e.GoodsClassD=d.GoodsClassID
WHERE GoodsClassName = '家用电器'
6.2.查询结果的并、交、差运算
6.2.1.并运算
并运算可将两个或多个查询语句的结果集合并为一个结果集,这个运算可以使用UNION运算符实现。
UNION是一个特殊的运算符,通过它可以实现让两个或更多的查询产生单一的结果集。而UNION操作与JOIN连接操作不同,如下所示是他们的不同点。
1.UNION操作
1.1 UNION更像是将一个查询结果追加到另一个查询结果中
1.2 UNION是垂直地合并数据(添加更多的行)
2. JOIN操作
2.1 JOIN操作是水平的合并数据(添加更多的列)
UNION谓词的语法格式:
SELECT 语句1 UNION [ALL]
SELECT 语句2 UNION [ALL]
...
SELECT 语句n
ALL表示在结果集中包含所有查询语句产生的全部记录,包括重复的记录。如果没有指定ALL,则系统默认是删除合并后结果集中的重复记录
注意:
- 所有要进行UNION操作的查询,其SELECT列表中列的个数必须相同,而且对应列的语义应该相同。
- 各查询语句中每个列的数据类型必须与其他查询中对应列的数据类型是隐式兼容的,即只要它们能进行隐式转换即可。合并后的结果采用第一个SELECT语句的列标题。
- 如果要对查询的结果进行排序,则ORDER BY子句应该写在最后一个查询语句之后,且排序的依据列应该是第一个查询语句中出现的列名。
案例:利用集合并运算查询姓李和姓王的客户的姓名和电话,并将查询的结果按电话升序排序
SELECT Name,Tel FROM Table_Customer
WHERE Name LIKE '李%'
UNION
SELECT Name,Tel FROM Table_Customer
WHERE Name LIKE '王%'
ORDER BY Tel ASC #升序排序,默认值
6.2.2.交运算
交运算将返回同时在两个集合中出现的记录,即返回两个查询结果集中各个列的值均相同的记录,并用这些记录构成交运算的结果。
INTERSECT格式:
SELECT 语句1
INTERSECT
SELECT 语句2
INTERSECT
...
SELECT 语句n
案例:设有购买表(顾客号,商品号,购买时间)。现要查询顾客A与顾客B购买相同的商品。
#方法1
SELECT 商品号 FROM 购买表 WHERE 顾客号 = 'A'
AND 商品号 IN(
SELECT 商品号 FROM 购买表 WHERE 顾客号 = 'B'
)
#方法2
SELECT 商品号 FROM 购买表 WHERE 顾客号 = 'A'
INTERSECT
SELECT 商品号 FROM 购买表 WHERE 顾客号 = 'B'
6.2.3.差运算
差运算将返回在第一个集合中有但第二个集合中没有的数据。实现差运算的SQL运算符EXCEPT
运算符EXCEPT格式:
SELECT 语句1
EXCEPT
SELECT 语句2
EXCEPT
...
SELECT 语句n
案例:设有购买表(顾客号,商品号,购买时间),先查询2019年顾客A购买了但顾客B没购买的商品
#方法一:
SELECT 商品号 FROM 购买表
WHERE YEAR(购买时间) = '2019' AND 顾客号 = 'A'
AND 商品号 NOT IN(
SELECT 商品号 FROM 购买表
WHERE YEAR(购买时间) = '2019' AND 顾客号 = 'B')
#方法二:
SELECT 商品号 FROM 购买表
WHERE YEAR(购买时间) = '2019' AND 顾客号 = 'A'
EXCEPT
SELECT 商品号 FROM 购买表
WHERE YEAR(购买时间) = '2019' AND 顾客号 = 'B'
6.3.相关子查询
- 如果一个SELECT语句是嵌套在一个SELECT、 INSERT、 UPDATE或DELETE语句中,则称为子查询或内层询,包含子查询的语句称为主查询或外层查询。
子查询类型:
- WHERE表达式 [NOT] IN子查询
- WHERE表达式 比较运算符子查询
- WHERE表达式 [NOT] EXISIT子查询
注意:子查询一般用在外层查询的WHERE子句或HAVING子句中,与比较运算符或逻辑运算符起构成询条件。对于返回结果为单值的子查询语句,可以出现在任何允许使用表达式的地方。
- 使用子查询进行基于集合的测试
- 说明:使用子查询进行基于集合的测试时,通过运算符IN和NOT IN,将个表达式的值与子查询返回的结果集进行比较。其形式为: WHERE表达式[NOT] IN (子查询)
- 步骤:
2.1 先执行子查询
2.2 在子查询的结果基础执行外层查询- 注意:子查询返回的结果实际上就是一个集合。外层查询就是在这个集合上使用IN运算符进行比较
案例:查询与“王晓”在同一个地址(区相同)的顾客的姓名和所在地址。前边用自连接的形式实现了这个查询,现在用子查询实现。
SELECT Cname,Address
FROM Table_Customer
WHERE Address IN (
SELECT Address FROM Table_Customer
WHERE Cname = '王晓')
AND Cname!='王晓'
- 使用子查询进行比较测试
- 说明:通过比较运算符( =、<>、<、>、<=、>=),将一个表达式的值与子查询返回的单值进行比较。形式为: WHERE表达式比较运算符(子查询)。
- 比较运算结果
2.1 TRUE
2.2 FALSE- 注意:使用子查询进行的比较测试要求子查询语句必须是返回单值的查询语句
案例:查询单价最高的商品名称和单价
SELECT GoodsName,SaleUnitPrice
FROM Table_Goods a
WHERE SaleUnitPrice = (
SELECT MAX(SaleUnitPrice)
FROM Table_Goods )
注意:由于聚合函数不能出现在WHERE子句中,因此,当一个列的值与一个聚合函数的结果进行比较时,必须用子查询先得到聚合函数的结果,然后在此结果基础之上再执行外层查询的比较。子查询的查询条件不依赖于外层查询,称这样的子查询为不相关子查询或嵌套子查询。
- 使用子查询进行存在性测试
使用子查询进行存在性测试时,通常使用EXISTS谓词,其形式为: WHERE [NOT] EXISTS(子查询)
子查询存在性测试
- 带EXISTS谓词的子查询不返回查询的结果.只产生逻辑真值和逻辑假值
- EXISTS含义:当子查询中有满足条件的数据时, EXISTS返回真值,否则返回假值
- NOT EXISTS含义:当子查询中有满足条件的数据时,NOT EXISTS返回假值;不存在满足条件的数据时,NOT EXISTS返回真值
案例:查询购买了单价高于2000元商品的顾客的会员卡号
SELECT DISTINCT CardID
FROM Table_SaleBillWHERE EXISTS (
SELECT * FROM Table_SaleBillDetail
WHERE SaleBillD = Table_SaleBill.SaleBilllD
AND UnitPrice > 2000)
- 使用子查询进行存在性测试
注意:
- 带EXISTS谓词的查询是先执行外层查询,然后再执行内层查询。由外层查询的值决定内层查询的结果;内层查询的执行次数由外层查询的结果数决定
- 由于带EXISTS的子查询只返回真或假值,因此在子查询里不必指定查询列表。所以在有EXISTS的子查询中,子查询中的目标列通常都用 “*"
6.4.其他形式的子查询
6.4.1.替代表达式的子查询
替代表达式的子查询是指在SELECT语句的选择列表中嵌人一个只返回一个标量值的SELECT语句,这个查询语句通常都是通过一个聚合函数来返回一个单值。
案例:查询G001顾客的姓名、地址以及该顾客购买商品的总次数
SELECT CName, Address
(SELECT COUNT(*) FROM Table_SaleBill a
JOIN Table_Customer b ON a. CardID = b.CardID
WHERE CustomerlD = 'G001') AS TotalTimes
FROM Table_Customer
WHERE CustomerlD ='G001'
6.4.2.派生表
- 派生表(也称内联视图)是将子查询作为一个表来处理,这个由子查询产生的新表就被称为“派生表”, 类似于临时表。
- 派生派的优点:使用派生表可以简化查询,从而避免使用临时表,而且相比手动生成临时表的方法性能更优越。
- 派生表与其他表一样出现在查询语句的FROM子句中。
案例:查询至少买了G001和G002两种商品的顾客号和顾客名
SELECT CustomerID,CName
FROM (
SELECT * FROM Table_SaleBill a
JOIN Table_SaleBillDetail b
ON a.SaleBilID = b.SaleBilD
WHERE GoodsID='G001' ) AS T1
JOIN (
SELECT * FROM Table_SaleBill a
JOIN Table_SaleBillDetail b
ON a.SaleBilD = b.SaleBillD
WHERE GoodsID= 'G0O2' ) AS T2
ON T1.CardID = T2.CardID
JOIN Table Customerc ON c.CardID = TI.CardID
6.5.其他一些查询功能
6.5.1.开窗函数
在SQL Server中,一组行被称为一 个窗口,开窗函数是指可以用于 “分区" 或 “分组” 计算的函数。这些函数结合OVER子句对组内的数据进行编号,并进行求和、计算平均值等统计。因此,从这个角度来说SUM、AVG以及ROW_NUMBER(对数据进行编号的函数) 等都可以称为开窗函数。
开窗函数可以分别应用于每个分区,把每个分区看成是一个窗口,并为每个分区进行计算。开窗函数必须放在OVER子句前边。开窗函数是在ISO SQL标准中定义的。
SQL Server的两种开窗函数:
- 排名开窗函数
- 聚合开窗函数
- 将OVER子句与聚合函数结合使用
1.1 OVER子句用于确定在应用关联的开窗函数之前对行集的分区和排序
1.2 将OVER子句与聚合函数结合使用的语法格式:
<OVER_CLAUSE>::=
OVER ([ PARTITION BY value_expression,...[n] )
各参数说明:
- PARTITION BY:将结果集划分为多个分区。开窗函数分别应用于每个分区,并为每个分区计算函数值
- value_expression:指定对行集进行分区所依据的列,该列必须是在FROM子句中生成的列,而且不能弓|用选择列表中的表达式或别名。value_expression可以是列表达式、替代表达式的子查询、标量函数或用户定义的变量
- 可以在单个查询中使用多个开窗函数,每个函数的OVER子句在分区和排序上可以不同
案例:设有订单明细表OrderDstail,结构如下
CREATE TABLE OrderDetail (
OrderID int NOT NULL
ProductID int NOT NULL
OrderQty smallint NOT NULL )
现要查询订单号、产品号、订购数量,每个订单的总订购数量以及每个产品的订购数量占该订单总订购数量的百分比,百分比保留到小数后2位
SELECT OrderID 订单号,ProductID 产品号,OrderQty 订单数量,
SUM (OrderQty)
OVER(Partition BY OrderID) as 总计,
CAST (1.0*OrderDetail)/SUM(OrderQty)
OVER (partition by OrderID)*100 as Decimal
FROM OderDetail
- 将OVER子句与排名函数一起使用
2.1 排名函数为分区中的每一行返回一 个排名值。根据所用函数的不同,某些行可能与其他行具有相同的排名值。排名函数具有不确定性。
2.2 SQL Server提供了4个名函数RANK的语法格式及特点如下:
- RANK
语法格式: RANK()
OVER( [ < partition_by_clause >
,…[n]]) < order_by_clause>
RANK( )函数返回结果集中每行数据在每个分区内的排名。每个分区内行的排名从1开始。如果排序时有值相同的行,则这些值相同的行具有相同的排名
案例:设有订单明细表OrderDetail ,结构如下:
CREATE TABLE OrderDetail (
OrderID int NOT NULL
ProductID int NOT NULL
OrderQty smallint NOT NULL )
现要查询订单号、产品号订购数量以及每个产品在每个订单中的订购数量排名
SELECT OrderID 订单号,ProductID 产品号,OrderQty 订单数量,
RANK() OVER (partition BY OrderID ORDER BY OrderQty DESC) AS 排名
FROM OrderDetail ORDER BY OrderID
- DENSE RANK
2.1 DENSE_RANK0函数与RANK()函数的作用基本一样,使用方法也一样
2.2 唯一区别是DENSE RANK()函数的排名中间没有任何间断,即该函数返回的是一个连续的整数值
案例:设有订单明细表OrderDetail ,结构如下:
CREATE TABLE OrderDetail (
OrderID int NOT NULL
ProductID int NOT NULL
OrderQty smallint NOT NULL )
现要查询订单号、产品号订购数量以及每个产品在每个订单中的订购数量排名
SELECT OrderID 订单号,ProductID 产品号,OrderQty 订单数量,
DENSE_RANK() OVER (partition BY OrderID ORDER BY OrderQty DESC) AS 排名
FROM OrderDetail ORDER BY OrderID
- NTILE
3.1 语法格式如下:
NTLE (integer expression ) OVER([ < partition_by_claluse > ] <
order_by_clause > )
3.2 作用是将有序分区中的行划分到指定数目的组中每个组有一个编号,编号从1开始。对于每行NTILE ( )函数返回此行所属的组的编号
案例:设有订单明细表OrderDetail ,结构如下:
CREATE TABLE OrderDetail (
OrderID int NOT NULL
ProductID int NOT NULL
OrderQty smallint NOT NULL )
现将该表数据按订购数量降序排列,并将该表数据划分到4个组中
SELECT OrderID,ProductID,OrderQty,NTILE(4)
OVER (ORDER BY OrderQty DESC)
AS FourGroup FROM OrderDetail
- ROW_NUMBER
4.1 语法格式:
ROW_NUMBER()
OVER ([ < partition_by_clause > ] <
order_by_clause > )
4.2 返回结果集中每个分区内行的序列号,每个分区的第一行从1开始
案例:查询"电冰箱”类商品的商品名,销售单价以及该商品在该类商品中的价格排名
SELECT GoodsName,SaleUnitPrice,
ROW_NUMBERO OVER(ORDER BY SaleUnitPrie DESC) AS'Number'
FROM Table_GoodsClass C JOIN Table_Goods G
ON C.GoodsClassID = G.GoodsClassID
WHERE GoodsClassName = '电冰箱'
6.5.2.公用表表达式
- 将查询语句产生的结果集指定一个临时命名的名字,这些命名的结果集就称为公用表表达式。命名后的公用表表达式就可在SELECT、INSERT、UPDATE、 DELETE等语句中被多次引用。公用表表达式还包括对自身的引用,这种表达式称为递归公用表表达式。
使用公用表表达式有以下四点好处:
- 可以定义递归公用表表达式
- GROUP BY子句可以直接作用在子查询所得的标量列上
- 可以在一个语句中多次引用公用表表达式
- 使数据操作代码更加清晰简洁
- 公用表表达式的格式及参数
WITH < common_table_expression > [, … n]
< common_table_expression > : :=
expression_name [( column name[, … n ]]
AS
(SELECT语句)
各参数说明:
- Expression_name:公用表表达式的标识符
expression_name必须与在同一WITH < common_table_expression>子句中定义的任何其他公用表表达式的名称不同,但该名可以与基本表或视图名相同。在查询中对expression_name的任何引用都会使用公用表表达式- column_name:在公用表表达式中指定列名。在一个CTE定义中不允许出现重名的列名
- SELECT语句:指定一个用其结果集填充到公用表表达式的SELECT语句
案例:定义一个统计每个会员的购买商品总次数的CTE并利用该CTE查询会员卡号和购买商品总次数
WITH BuyCount( CardID, Counts) AS (
SELECT CardID, COUNT(*)
FROM Table_SaleBillGROUP BY CardID )
SELECT CardID, Counts
FROM BuyCount
ORDER BY Counts;
注意:可以在一个WITH子句中定义多个CTE ,也可以在一一个查询中多次引用同一个CTE。
以上是关于数据库三级的主要内容,如果未能解决你的问题,请参考以下文章