子查询

子查询

SQL 支持嵌套查询(在查询内编写查询)。子查询(内部查询)的查询结果集被用于外部查询,外部查询的查询结果集返回给调用者。子查询分为自包含子查询相关子查询,自包含子查询不依赖于其所属的外部查询,相关子查询依赖于外部查询。子查询可以返回单个值(标量值)、多个值或是整个表结果。

自包含标量子查询

标量子查询是返回单个值的子查询——不管其是否是自包含的。这样的子查询可以出现在外部查询中单个值表达式能够出现的任何地方(WHERE、SELECT等)。

SELECT CommentText, ReplyText, CreateTime
FROM Comments A
WHERE A.PKArticle=(SELECT TOP(1) B.PKArticle FROM Articles B)

要使标量子查询有效,则必须保证标量子查询返回一个值。如果标量子查询返回多个值则查询失败。如果标量子查询没有返回值,则返回 NULL。与 NULL 比较会生成 UNKNOWN,并且查询筛选不返回筛选表达式计算为 UNKNOWN 的行,所以会返回一个空集合。

自包含多值子查询

多值的子查询是作为单个列,返回多个值的子查询——无论子查询是否是自包含的。谓词 IN、SOME、ANY、ALL 可以进行多值子查询操作。

SELECT CommentText, ReplyText, CreateTime
FROM Comments A
WHERE A.PKArticle IN (SELECT B.PKArticle FROM Articles B)

使用 IN 谓词,对于任意数量(空值、1个或多个)的返回值,查询都是有效的。可以使用 NOT 逻辑运算符否定 IN 谓词。在子查询中不用显示地指定一个 DISTINCT,因为数据库引擎足够智能,会考虑删除重复项。

相关子查询

相关子查询引用的表属性位于外部查询中,这意味着,该子查询依赖于外部查询并且无法单独调用。

SELECT CommentText, ReplyText, CreateTime
FROM Comments A
WHERE A.PKArticle IN (SELECT B.PKArticle FROM Articles B WHERE A.UserID=B.UserID)
EXISTS 谓词

T-SQL 支持一个叫做 EXISTS 的谓词,可以接受一个子查询作为输入。如果子查询返回任意行,谓词返回 TRUE,否则返回 FALSE(EXISTS 使用两值逻辑)。
EXISTS 谓词只关心匹配行的存在,而不管在 SELECT 列表中指定的属性。SQL Server 数据库引擎知道这些情况,并在优化时忽略子查询的 SELECT 列表。所以在优化时,这种情况中指定列通配符“”与指定一个常量相比,没有任何负面影响。不过在对元数据信息扩展通配符的解析过程中可能会涉及一些小的额外消耗,但是这种额外解析成本小到你可能几乎注意不到它。但是 EXISTS(SELECT FROM …)比 EXISTS(SELECT 1 FROM …)更直观,节省微不足道的与星号(*)解析相关的额外成本,相对于牺牲代码可读性的成本是不值一提的。

其他

返回前一个或下一个

“前一个”逻辑等效于“小于当前值的最大值”,第一个之前不会有文章,子查询为第一个 PrevArticleID 返回 NULL 值。例:

SELECT
    A.ArticleID, A.Title, A.Body,
    PrevArticleID=(SELECT MAX(B.ArticleID) FROM Articles B WHERE B.ArticleID<A.ArticleID)
FROM Articles A

“下一个”逻辑等效于“大于当前值的最小值”,最后一个之后不会有文章,子查询为最后一个 PrevArticleID 返回 NULL 值。例:

SELECT
    A.ArticleID, A.Title, A.Body,
    PrevArticleID=(SELECT MIN(B.ArticleID) FROM Articles B WHERE B.ArticleID>A.ArticleID)
FROM Articles A
使用运行聚合

运行聚合是随着时间积累值的聚合。如需要返回订单ID、金额、创建时间和经过时间的运行总金额:

SELECT
    A.OrderID, A.Amount, A.CreateTime,
    RunAmount=(SELECT SUM(B.Amount) FROM Orders B WHERE B.CreateTime<=A.CreateTime)
FROM Orders A ORDER BY A.CreateTime
NULL 故障

当对一个至少返回一个 NULL 的子查询使用谓词时,外部查询总是返回空集合。因为在包含 NULL 的集合中,你永远不能肯定值没有出现在集合中(NULL 使用 = 判断为 UNKNOWN,不知道不代表不相等)。

SELECT A.custid, A.companyname
FROM Sales.Customers A WHERE A.custid NOT IN(SELECT B.custid FROM Sales.Orders B)

如果想要避免这种故障,第一可以强制数据完整性,将列定义为 NOT NULL。第二可以通过在子查询 WHERE 子句中使用 IS NOT NULL 显示排除。第三可以通过 NOT EXISTS 谓词替换 NOT IN 隐式的排除 NULL 标记。因为 IN 使用三值逻辑,而 EXISTS 使用两值逻辑,EXISTS 总是返回 TRUE 或 FALSE,并且绝不会返回 UNKNOWN。

SELECT A.custid, A.companyname
FROM Sales.Customers A
WHERE A.custid NOT IN(SELECT B.custid FROM Sales.Orders B WHERE B.custid IS NOT NULL)

SELECT A.custid, A.companyname
FROM Sales.Customers A
WHERE NOT EXISTS(SELECT * FROM Sales.Orders B WHERE A.custid=B.custid)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值