OUTER/CROSS 不带 FROM 子句的 APPLY 子查询
OUTER/CROSS APPLY Subquery without FROM clause
大多数讨论 OUTER|CROSS APPLY
的在线文档或教程描述如下内容:
SELECT columns
FROM table OUTER|CROSS APPLY (SELECT … FROM …);
子查询通常是完整的 SELECT … FROM …
查询。
我一定在某处读到子查询不需要 FROM
,在这种情况下,列似乎来自主查询:
SELECT columns
FROM table OUTER|CROSS APPLY (SELECT … );
因为我经常使用它作为预计算列的方法。
问题是,如果从子查询中省略 FROM
,到底发生了什么?它是别的东西的缩写吗?我发现它确实 not 与主要 table.
的意思相同
我这里有一个样本:http://sqlfiddle.com/#!18/0188f7/4/1
首先考虑
SELECT o.name, o.type
FROM sys.objects o
现在考虑
SELECT o.name, (SELECT o.type) AS type
FROM sys.objects o
没有 FROM
的 SELECT
就像是从假想的单行 table 中选择一样。以上并没有改变标量子查询的结果,它只是作为一个相关的子查询,并使用来自外部查询的值。
APPLY
的行为方式相同。来自外部查询的对列的引用只是作为相关参数传入。所以这和
是一样的
SELECT o.name, ca.type
FROM sys.objects o
CROSS APPLY (SELECT o.type) AS ca
但是 APPLY
通常比 SELECT
中的标量子查询更有能力(因为它可以扩展行或从结果中删除行)
您提到的不是SUBQUERY。它是单独的 table 表达式。是否在正确的表达式中使用 FROM 子句是个问题。
- 如果您在右 table 表达式中使用 FROM 子句,那么您就获得了右 table 表达式中的数据源。
- 如果您不在右侧表达式中使用 FROM 子句,则您的数据源来自左侧 table 表达式。
首先我们将了解什么是 APPLY 运算符。 Reference BOL
Using APPLY
Both the left and right operands of the APPLY operator are table
expressions. The main difference between these operands is that the
right_table_source can use a table-valued function that takes a column
from the left_table_source as one of the arguments of the function.
The left_table_source can include table-valued functions, but it
cannot contain arguments that are columns from the right_table_source.
The APPLY operator works in the following way to produce the table
source for the FROM clause:
Evaluates right_table_source against each row of the left_table_source to produce rowsets.
The values in the right_table_source depend on left_table_source.
right_table_source can be represented approximately this way:
TVF(left_table_source.row), where TVF is a table-valued function.
Combines the result sets that are produced for each row in the evaluation of right_table_source with the left_table_source by
performing a UNION ALL operation.
The list of columns produced by the result of the APPLY operator is
the set of columns from the left_table_source that is combined with
the list of columns from the right_table_source.
根据您使用 APPLY 运算符的方式,它将表现为相关子查询或 CROSS JOIN
- 在右侧table表达式
中使用左侧table表达式的值
-- without FROM (similar to Correlated Subquery)
SELECT id, data, value
FROM test OUTER APPLY(SELECT data*10 AS value) AS sq;
- 没有在右 table 表达式
中使用左 table 表达式的值
-- FROM table (Similar to cross join)
SELECT id, data, value
FROM test OUTER APPLY(SELECT data*10 AS value FROM test) AS sq;
省略 FROM
语句并不特定于 CROSS
/OUTER
APPLY
;任何有效的 SQL select 语句都可以省略它。如果不使用 FROM,您就没有数据来源,因此您无法在该来源中指定列。相反,您只能 select 已经存在的值;是在语句本身中定义的常量,或者在某些情况下(例如子查询)从查询的其他部分引用的列。
如果您熟悉 Oracle 的 Dual
table,这将更容易理解;一个 table 有 1 行。在 MS SQL 中,table 看起来像这样:
-- Ref: https://blog.sqlauthority.com/2010/07/20/sql-server-select-from-dual-dual-equivalent/
CREATE TABLE DUAL
(
DUMMY VARCHAR(1) NOT NULL
, CONSTRAINT CHK_ColumnD_DocExc CHECK (DUMMY = 'X') -- ensure this column can only hold the value X
, CONSTRAINT PK_DUAL PRIMARY KEY (DUMMY) -- ensure we can only have unique values... combined with the above means we can only ever have 1 row
)
GO
INSERT INTO DUAL (DUMMY)
VALUES ('X')
GO
然后您可以select 1 one, 'something else' two from dual
。你并没有真正使用双重;只是确保你有一个 table 总是 return 恰好 1 行。
现在在 SQL 任何地方你省略了一个 FROM
语句认为该语句好像它说 FROM DUAL
/ 它具有相同的含义,只有 SQL 允许更多shorthand方法。
更新
您在评论中提到您没有看到如何在子查询中引用原始语句中的列(例如,您在使用 APPLY
时可能会看到的那种)。下面的代码在没有 APPLY
场景的情况下显示了这一点。诚然,这里的演示代码不是您曾经使用过的东西(因为您可以在原始语句上 where Something like '%o%'
而不需要 subquery/in 语句),但出于说明目的,它显示了完全相同的场景就像您的 APPLY 场景一样;也就是说,该语句只是 returning 当前行的 SOMETHING
的值。
declare @someTable table (
Id bigint not null identity(1,1)
, Something nvarchar(32) not null
)
insert @someTable (Something) values ('one'), ('two'), ('three')
select *
from @someTable x
where x.Something in
(
-- this subquery references the SOMETHING column from above, but doesn't have a FROM statement
-- note: there is only 1 value at a time for something here; not all 3 values at once; it's the same single value as Something as we have before the in keyword above
select Something
where Something like '%o%'
)
大多数讨论 OUTER|CROSS APPLY
的在线文档或教程描述如下内容:
SELECT columns
FROM table OUTER|CROSS APPLY (SELECT … FROM …);
子查询通常是完整的 SELECT … FROM …
查询。
我一定在某处读到子查询不需要 FROM
,在这种情况下,列似乎来自主查询:
SELECT columns
FROM table OUTER|CROSS APPLY (SELECT … );
因为我经常使用它作为预计算列的方法。
问题是,如果从子查询中省略 FROM
,到底发生了什么?它是别的东西的缩写吗?我发现它确实 not 与主要 table.
我这里有一个样本:http://sqlfiddle.com/#!18/0188f7/4/1
首先考虑
SELECT o.name, o.type
FROM sys.objects o
现在考虑
SELECT o.name, (SELECT o.type) AS type
FROM sys.objects o
没有 FROM
的 SELECT
就像是从假想的单行 table 中选择一样。以上并没有改变标量子查询的结果,它只是作为一个相关的子查询,并使用来自外部查询的值。
APPLY
的行为方式相同。来自外部查询的对列的引用只是作为相关参数传入。所以这和
SELECT o.name, ca.type
FROM sys.objects o
CROSS APPLY (SELECT o.type) AS ca
但是 APPLY
通常比 SELECT
中的标量子查询更有能力(因为它可以扩展行或从结果中删除行)
您提到的不是SUBQUERY。它是单独的 table 表达式。是否在正确的表达式中使用 FROM 子句是个问题。
- 如果您在右 table 表达式中使用 FROM 子句,那么您就获得了右 table 表达式中的数据源。
- 如果您不在右侧表达式中使用 FROM 子句,则您的数据源来自左侧 table 表达式。
首先我们将了解什么是 APPLY 运算符。 Reference BOL
Using APPLY
Both the left and right operands of the APPLY operator are table expressions. The main difference between these operands is that the right_table_source can use a table-valued function that takes a column from the left_table_source as one of the arguments of the function. The left_table_source can include table-valued functions, but it cannot contain arguments that are columns from the right_table_source.
The APPLY operator works in the following way to produce the table source for the FROM clause:
Evaluates right_table_source against each row of the left_table_source to produce rowsets.
The values in the right_table_source depend on left_table_source. right_table_source can be represented approximately this way: TVF(left_table_source.row), where TVF is a table-valued function.
Combines the result sets that are produced for each row in the evaluation of right_table_source with the left_table_source by performing a UNION ALL operation.
The list of columns produced by the result of the APPLY operator is the set of columns from the left_table_source that is combined with the list of columns from the right_table_source.
根据您使用 APPLY 运算符的方式,它将表现为相关子查询或 CROSS JOIN
- 在右侧table表达式 中使用左侧table表达式的值
-- without FROM (similar to Correlated Subquery)
SELECT id, data, value
FROM test OUTER APPLY(SELECT data*10 AS value) AS sq;
- 没有在右 table 表达式 中使用左 table 表达式的值
-- FROM table (Similar to cross join)
SELECT id, data, value
FROM test OUTER APPLY(SELECT data*10 AS value FROM test) AS sq;
省略 FROM
语句并不特定于 CROSS
/OUTER
APPLY
;任何有效的 SQL select 语句都可以省略它。如果不使用 FROM,您就没有数据来源,因此您无法在该来源中指定列。相反,您只能 select 已经存在的值;是在语句本身中定义的常量,或者在某些情况下(例如子查询)从查询的其他部分引用的列。
如果您熟悉 Oracle 的 Dual
table,这将更容易理解;一个 table 有 1 行。在 MS SQL 中,table 看起来像这样:
-- Ref: https://blog.sqlauthority.com/2010/07/20/sql-server-select-from-dual-dual-equivalent/
CREATE TABLE DUAL
(
DUMMY VARCHAR(1) NOT NULL
, CONSTRAINT CHK_ColumnD_DocExc CHECK (DUMMY = 'X') -- ensure this column can only hold the value X
, CONSTRAINT PK_DUAL PRIMARY KEY (DUMMY) -- ensure we can only have unique values... combined with the above means we can only ever have 1 row
)
GO
INSERT INTO DUAL (DUMMY)
VALUES ('X')
GO
然后您可以select 1 one, 'something else' two from dual
。你并没有真正使用双重;只是确保你有一个 table 总是 return 恰好 1 行。
现在在 SQL 任何地方你省略了一个 FROM
语句认为该语句好像它说 FROM DUAL
/ 它具有相同的含义,只有 SQL 允许更多shorthand方法。
更新
您在评论中提到您没有看到如何在子查询中引用原始语句中的列(例如,您在使用 APPLY
时可能会看到的那种)。下面的代码在没有 APPLY
场景的情况下显示了这一点。诚然,这里的演示代码不是您曾经使用过的东西(因为您可以在原始语句上 where Something like '%o%'
而不需要 subquery/in 语句),但出于说明目的,它显示了完全相同的场景就像您的 APPLY 场景一样;也就是说,该语句只是 returning 当前行的 SOMETHING
的值。
declare @someTable table (
Id bigint not null identity(1,1)
, Something nvarchar(32) not null
)
insert @someTable (Something) values ('one'), ('two'), ('three')
select *
from @someTable x
where x.Something in
(
-- this subquery references the SOMETHING column from above, but doesn't have a FROM statement
-- note: there is only 1 value at a time for something here; not all 3 values at once; it's the same single value as Something as we have before the in keyword above
select Something
where Something like '%o%'
)