为什么 T-SQL CROSS APPLY 有时表现得像 LEFT JOIN

问题描述

我读过的大多数文档都表明 CROSS APPLY 的行为方式与 INNER JOIN 类似,只有当两个源表中都有匹配的行时,才会将行包含在输出中。

然而,情况似乎并非总是如此,例如,如果您运行以下 sql 查询,结果将包含 3 行,其中由于右侧没有行,其中之一包含许多 NULL-手台:

CREATE TABLE #Order
(
    Id          int PRIMARY KEY
)

CREATE TABLE #OrderItem
(
    OrderId     int NOT NULL,Price       decimal(18,2) NOT NULL
)

INSERT INTO #Order
VALUES(1),(2),(3)

INSERT INTO #OrderItem
VALUES(1,10),(1,20),(3,100)

SELECT *
FROM #Order o
CROSS APPLY
(
    SELECT SUM(Price) AS TotalPrice,COUNT(*) AS Items,MIN(Price) AS MinPrice
    FROM #OrderItem
    WHERE OrderId = o.Id
) t

DROP TABLE #Order
DROP TABLE #OrderItem

有人知道这是为什么吗?

解决方法

TL;DR;

发生这种情况的原因是聚合是标量聚合。


有两种类型的聚合:

  • 矢量聚合

    • 需要一个 GROUP BY 子句

    • 如果输入没有行,则根本不返回任何行

  • 标量聚合

    • GROUP BY 子句

    • 总是至少返回一行,即使没有输入行。 COUNT 返回 0,其他返回 NULL

您使用的是标量聚合,因此总是返回一行。

要获得矢量聚合,您需要添加一个 GROUP BY

SELECT *
FROM #Order o
CROSS APPLY
(
    SELECT SUM(oi.Price) AS TotalPrice,COUNT(*) AS Items,MIN(oi.Price) AS MinPrice
    FROM #OrderItem oi
    WHERE oi.OrderId = o.Id   -- always specify inner table in column references
    GROUP BY ()   -- the empty set
-- alternatively
    GROUP BY oi.OrderId
) t

另见@PaulWhite 的这篇优秀文章: Fun with Scalar and Vector Aggregates

,

有人知道这是为什么吗?

因为无论是否有任何匹配的行,您正在申请的查询都会返回一行,因为它是一个聚合查询。

,

您似乎认为当没有适用的行时,聚合不会返回任何行。如果没有 GROUP BY 子句,则情况并非如此。采取以下无意义的查询:

SELECT COUNT(*) AS C,SUM(object_ID) AS S,MAX(object_ID) AS M
FROM sys.tables
WHERE [name]= N'sdfhjklsdgfgjklb807ty3480A645*)&TY0';

现在,除非你给你的一个对象取了一个非常愚蠢的名字,否则你仍然会得到一个只有这里的结果集:

C           S           M
----------- ----------- -----------
0           NULL        NULL

因此,对于您的查询,您的子查询中的每一行都会得到一行,因为它只包含聚合而不包含 GROUP BY

如果您不想要 Id 2 的行,那么您可以使用横向子查询或 WHERE

SELECT *
FROM #Order O
     JOIN (SELECT sq.OrderId,SUM(sq.Price) AS TotalPrice,MIN(sq.Price) AS MinPrice
           FROM #OrderItem sq
           GROUP BY sq.OrderID) OI ON O.Id = OI.OrderID;

SELECT *
FROM #Order o
     CROSS APPLY(SELECT SUM(ca.Price) AS TotalPrice,MIN(ca.Price) AS MinPrice
                 FROM #OrderItem ca
                 WHERE ca.OrderId = o.Id) OI
WHERE OI.Items > 0;

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...