使用标量或多值子查询

已完成

标量子查询是外部查询中的内部 SELECT 语句,写入后可返回单个值。 标量子查询可用于 T-SQL 语句中允许单值表达式的任何位置,例如 SELECT 子句、WHERE 子句、HAVING 子句,甚至是 FROM 子句。 此外,它们还可用于 UPDATE 或 DELETE 等数据修改语句。

顾名思义,多值子查询可以返回多个行。 不过,它们仍会返回单列。

标量子查询

假设要检索上次下单的详细信息,假定它是具有最高“SalesOrderID”值的订单。

要查找最高的“SalesOrderID”值,可以使用以下查询:

SELECT MAX(SalesOrderID)
FROM Sales.SalesOrderHeader

此查询将返回一个指示“SalesOrderHeader”表中“OrderID”最高值的值。

要获取该订单的详细信息,可能需要根据上述查询返回的任何值筛选“SalesOrderDetails”表。 通过将检索最高“SalesOrderID”的查询嵌套在检索订单详细信息的查询的 WHERE 子句中,可以完成此项任务。

SELECT SalesOrderID, ProductID, OrderQty
FROM Sales.SalesOrderDetail
WHERE SalesOrderID = 
   (SELECT MAX(SalesOrderID)
    FROM Sales.SalesOrderHeader);

要写入标量子查询,请考虑以下准则:

  • 要将查询表示为子查询,请用括号将其括起来。
  • Transact-SQL 中支持多级子查询。 在此模块中,我们仅考虑两级查询(一个外部查询中有一个内部查询),但最多支持 32 个级别。
  • 如果子查询没有返回任何行(空集),则子查询的结果是 NULL。 如果方案中可以不返回任何行,则应确保外部查询除处理其他预期结果之外,还能正常处理 NULL。
  • 内部查询一般应返回单列。 在子查询中选择多个列几乎都会出错。 唯一例外是,使用 EXISTS 关键字引入子查询时。

标量子查询可用于查询中涉及值的任何位置,包括 SELECT 列表。 例如,我们可以将检索到最新订单详细信息的查询扩展到包括已订购商品的平均数量,这样我们就能将最新订单的订购数量与所有订单的平均订购数量进行比较。

SELECT SalesOrderID, ProductID, OrderQty,
    (SELECT AVG(OrderQty)
     FROM SalesLT.SalesOrderDetail) AS AvgQty
FROM SalesLT.SalesOrderDetail
WHERE SalesOrderID = 
    (SELECT MAX(SalesOrderID)
     FROM SalesLT.SalesOrderHeader);

多值子查询

多值子查询非常适合返回使用 IN 运算符的结果。 以下假设示例将返回加拿大客户下达的所有订单的“CustomerID”和“SalesOrderID”值。

SELECT CustomerID, SalesOrderID
FROM Sales.SalesOrderHeader
WHERE CustomerID IN (
    SELECT CustomerID
    FROM Sales.Customer
    WHERE CountryRegion = 'Canada');

在此示例中,如果只执行内部查询,则会返回“CustomerID”值列,每行代表加拿大的一位客户。

很多情况下,可以使用 join 轻松写入多值子查询。 例如,以下查询使用 join 来返回与前面示例相同的结果:

SELECT c.CustomerID, o.SalesOrderID
FROM Sales.Customer AS c
JOIN Sales.SalesOrderHeader AS o
    ON c.CustomerID = o.CustomerID
WHERE c.CountryRegion = 'Canada';

那么,怎么决定是以 JOIN 方式写入涉及多个表的查询,还是使用子查询? 有时,这取决于你哪种更熟练。 大多数可轻松转换为 JOIN 的嵌套查询,实际上都会在内部转换为 JOIN。 对于这类查询,使用哪种方式写入并无实际差别。

应谨记的一项限制是,使用嵌套查询时,返回到客户端的结果只能包含外部查询中的列。 因此,如果需要返回两个表中的列,则应使用 JOIN 来写入查询。

最后,在某些情况下,内部查询需要执行示例中的简单检索无法做到的更复杂的操作。 使用 JOIN 重写复杂子查询可能非常困难。 许多 SQL 开发人员发现,子查询非常适合用于复杂处理,因为使用它们可以将处理分解为更小的步骤。