本文通过提供实例简要说明了如何在SQL中编写子查询。
简介
SQL查询是一条用来请求存储在关系型数据库中的表的数据的命令。一般来说,一个SQL查询至少包含两个或多个子句。
- 选择子句:该子句用于指定结果集元数据(列、固定值、表达式)。
- From子句:该子句用于指定我们要查询的数据源。一个数据源可以是一个单一的表或视图,也可以有更复杂的形式。
- Where子句:该子句用于指定SQL查询中需要的数据过滤操作。
下面的章节将解释如何在SQL中的SELECT、FROM和WHERE子句中编写子查询。
在SELECT子句中编写子查询
首先,我们将解释如何在SQL的SELECT子句中编写子查询。即使在SELECT子句中支持写子查询,开发者一旦决定使用子查询,也必须仔细编写查询,因为它降低了查询性能。
让我们假设,我们需要写一个SQL查询来检索Stack overflow数据库中的前十名用户以及每个用户获得的最新徽章。让我们考虑以下查询。
SELECT TOP (10) [Id]
,[DisplayName]
,(SELECT TOP 1 [Name] FROM [dbo].[Badges] badges WHERE badges.UserId = users.Id Order By [Date] Desc) as Latest_Badge
FROM [StackOverflow2013].[dbo].[Users] users
图1 - 在SELECT子句中写一个子查询
把子查询写成一列并不意味着对从Users表中检索的每一条记录都要执行子查询。如果我们显示估计的执行计划,它将显示徽章数据被检索出来,然后与Users表进行左键连接。
图2 - 执行计划
在FROM子句中编写子查询
在这一节中,我们将说明如何在FROM子句中写一个SQL子查询。
我们可以使用一个SQL子查询作为数据源,而不是在FROM子句中使用表或视图的名称,同时注意到需要指定一个别名。让我们尝试用另一种方式来写前面的查询。
SELECT [Id]
,[DisplayName]
,(SELECT TOP 1 [Name] FROM [dbo].[Badges] badges WHERE badges.UserId = users.Id ORDER BY [Date] DESC) as Latest_Badge
FROM (SELECT TOP 10 * FROM [StackOverflow2013].[dbo].[Users] ) users
我们决定不在SELECT子句中写TOP 10选项,而是强制SQL Server查询优化器在连接Users表和Badges数据之前执行TOP 10数据检索操作,如前所示。
图3 - 在FROM子句中写一个子查询
在下面的截图中,你可以注意到TOP操作是如何在扫描Users聚类索引后直接执行的,而在前面的部分,它是作为最后一步执行的。
图4 - 执行计划
- 注意:这并不意味着第二种方法比第一种方法更好。这个例子只是用来说明将TOP操作符移到子查询中的影响
在JOINS中编写子查询
此外,我们还可以在使用子查询时在FROM子句中添加连接。让我们用下面的例子来说明,当需要连接时,如何在SQL中的FROM子句中写一个子查询。
SELECT users.[Id]
,[DisplayName]
,latest_posts.[CreationDate]
FROM [StackOverflow2013].[dbo].[Users] users INNER JOIN
(SELECT TOP 10 [OwnerUserId],[CreationDate] FROM [dbo].[Posts] ORDER BY [CreationDate] DESC) latest_posts on users.Id = latest_posts.OwnerUserId
在上面的例子中,我们使用了一个子查询来检索最新的十个帖子和它们的创建日期。然后,我们将结果与用户表连接起来,得到帖子所有者的信息。
图5 - 在FROM子句中使用SQL子查询并进行连接
在WHERE子句中编写子查询
为了说明如何在SQL中的WHERE子句中编写子查询,我们将编辑前面的查询,以检索在Stack overflow数据库中发布最新10个帖子的用户。让我们使用下面这个查询。
SELECT [Id]
,[DisplayName]
FROM [StackOverflow2013].[dbo].[Users] users
WHERE [Id] IN (SELECT TOP 10 [OwnerUserId] FROM [dbo].[Posts] ORDER BY [CreationDate] DESC)
在这个查询中,我们把子查询从FROM子句移到了WHERE子句中,并且使用IN操作符根据子查询的结果来过滤Users Id。
图6 - 在WHERE子句中写一个SQL子查询
替代方法
在SQL中使用子查询有很多替代方法。
- 使用视图:在某些情况下,视图可以取代子查询,使查询看起来更简单。这个选项并不影响或提高查询性能,除非是有索引的视图。
- 使用普通表表达式(CTE):普通表表达式是子查询的一种替代方法。
摘要
这篇文章说明了如何在SQL中的SELECT、FROM和WHERE子句中写一个子查询。即使这种能力在结构化查询语言(SQL)中很有趣,开发者也应该谨慎使用,因为它可能会影响查询的性能。此外,在需要时创建一些索引以提高查询性能是至关重要的。