如何在SQL中编写子查询

185 阅读4分钟

本文通过提供实例简要说明了如何在SQL中编写子查询。

简介

SQL查询是一条用来请求存储在关系型数据库中的表的数据的命令。一般来说,一个SQL查询至少包含两个或多个子句。

  1. 选择子句:该子句用于指定结果集元数据(列、固定值、表达式)。
  2. From子句:该子句用于指定我们要查询的数据源。一个数据源可以是一个单一的表或视图,也可以有更复杂的形式。
  3. 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

how to write a subquery in SQL within the SELECT clause

图1 - 在SELECT子句中写一个子查询

把子查询写成一列并不意味着对从Users表中检索的每一条记录都要执行子查询。如果我们显示估计的执行计划,它将显示徽章数据被检索出来,然后与Users表进行左键连接。

query execution plan

图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数据检索操作,如前所示。

how to write a subquery in SQL within the FROM clause

图3 - 在FROM子句中写一个子查询

在下面的截图中,你可以注意到TOP操作是如何在扫描Users聚类索引后直接执行的,而在前面的部分,它是作为最后一步执行的。

query execution plan

图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

在上面的例子中,我们使用了一个子查询来检索最新的十个帖子和它们的创建日期。然后,我们将结果与用户表连接起来,得到帖子所有者的信息。

how to write a subquery in SQL within the FROM clause with joins

图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。

how to write a subquery in SQL within the WHERE clause

图6 - 在WHERE子句中写一个SQL子查询

替代方法

在SQL中使用子查询有很多替代方法。

  1. 使用视图:在某些情况下,视图可以取代子查询,使查询看起来更简单。这个选项并不影响或提高查询性能,除非是有索引的视图。
  2. 使用普通表表达式(CTE):普通表表达式是子查询的一种替代方法。

摘要

这篇文章说明了如何在SQL中的SELECT、FROM和WHERE子句中写一个子查询。即使这种能力在结构化查询语言(SQL)中很有趣,开发者也应该谨慎使用,因为它可能会影响查询的性能。此外,在需要时创建一些索引以提高查询性能是至关重要的。