没有 FROM 子句的 OUTER/CROSS APPLY 子查询
Posted
技术标签:
【中文标题】没有 FROM 子句的 OUTER/CROSS APPLY 子查询【英文标题】:OUTER/CROSS APPLY Subquery without FROM clause 【发布时间】:2020-02-20 07:50:14 【问题描述】:大多数讨论 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 与主表中的含义相同。
我这里有一个示例:http://sqlfiddle.com/#!18/0188f7/4/1
【问题讨论】:
您好,它会为您提供函数而不是内联查询吗? 引擎需要 from 子句才能知道数据的来源。如果您知道自己的值是什么,则省略 FROM 工作,例如 SELECT 1, SELECT 'no from Clause'。如果您有一个需要查询的表,则需要一个 from 子句"The question is what really happens if the FROM is omitted from the sub query?"
你不能自己运行一下看看发生了什么吗?
没有FROM
的SELECT
就像从一个虚构的单行表中选择一样。在APPLY
的情况下,如果您引用主表中的列,那么它只会使用外部查询中当前行的值作为相关参数。
我注意到 1 票反对,1 票结束此问题。有人愿意解释一下,还是这只是一个肇事逃逸?
【参考方案1】:
首先考虑
SELECT o.name, o.type
FROM sys.objects o
现在考虑
SELECT o.name, (SELECT o.type) AS type
FROM sys.objects o
没有FROM
的SELECT
就像从虚构的单行表中进行选择一样。上述内容不会改变结果,标量子查询只是充当相关子查询并使用外部查询的值。
APPLY
的行为方式相同。对来自外部查询的列的引用只是作为相关参数传入。所以这是一样的
SELECT o.name, ca.type
FROM sys.objects o
CROSS APPLY (SELECT o.type) AS ca
但APPLY
通常比SELECT
中的标量子查询更有能力(因为它可以扩展一行或从结果中删除行)
【讨论】:
好的,这给我带来了一个新的查询,为什么 apply 会这样工作,为什么每个子查询都不能像那样进行【参考方案2】:你提到的不是SUBQUERY。它是单独的表表达式。在正确的表达式中使用 FROM 子句是否有问题。
-
如果您在右表表达式中使用 FROM 子句,那么您就有了右表表达式中数据的来源。
如果在右表达式中不使用 FROM 子句,则数据源来自左表表达式。
首先我们来看看什么是APPLY操作符。 Reference BOL
使用应用
APPLY 运算符的左右操作数都是表 表达式。 这些操作数之间的主要区别在于 right_table_source 可以使用接受一列的表值函数 来自 left_table_source 作为函数的参数之一。 left_table_source 可以包含表值函数,但它 不能包含来自 right_table_source 的列的参数。
APPLY 运算符按以下方式工作以生成表 FROM 子句的来源:
根据 left_table_source 的每一行评估 right_table_source 以生成行集。
right_table_source 中的值取决于 left_table_source。 right_table_source 可以大致这样表示: TVF(left_table_source.row),其中 TVF 是一个表值函数。
将在评估 right_table_source 和 left_table_source 时为每一行生成的结果集 执行 UNION ALL 操作。
APPLY 运算符的结果产生的列列表是 left_table_source 中与 right_table_source 中的列列表。
根据您使用 APPLY 运算符的方式,它将表现为相关子查询或 CROSS JOIN
-
在右表表达式中使用左表表达式的值
-- without FROM (similar to Correlated Subquery)
SELECT id, data, value
FROM test OUTER APPLY(SELECT data*10 AS value) AS sq;
-
不在右表表达式中使用左表表达式的值
-- FROM table (Similar to cross join)
SELECT id, data, value
FROM test OUTER APPLY(SELECT data*10 AS value FROM test) AS sq;
【讨论】:
啊,那句话,这些操作数之间的主要区别在于,right_table_source 可以使用表值函数,该函数将 left_table_source 中的列作为函数的参数之一。 回答问题。谢谢。【参考方案3】:省略FROM
语句并不特定于CROSS
/OUTER
APPLY
;任何有效的 SQL 选择语句都可以省略它。如果不使用 FROM,您就没有数据来源,因此您无法在该来源中指定列。相反,您只能选择已经存在的值;是在语句本身中定义的常量,或者在某些情况下(例如子查询)从查询的其他部分引用的列。
如果您熟悉Oracle 的Dual
表,这将更容易理解;有 1 行的表。在 MS SQL 中,该表如下所示:
-- 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
。你并没有真正使用双重;只是确保你有一个总是会返回 1 行的表。
现在在 SQL 中,如果您省略了 FROM
语句,请将该语句视为表示 FROM DUAL
/ 它具有相同的含义,只有 SQL 允许这种更简写的方法。
更新
您在 cmets 中提到,您看不到如何在子查询中引用原始语句中的列(例如,您在使用 APPLY
时可能会看到的那种)。下面的代码在没有APPLY
场景的情况下显示了这一点。诚然,这里的演示代码不是您曾经使用过的东西(因为您可以在原始语句上使用 where Something like '%o%'
而不需要 subquery/in 语句),但出于说明目的,它显示了与您完全相同的场景得到你的应用场景;即该语句只是返回当前行的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%'
)
【讨论】:
据我所知,SQL Server 不像 Oracle 那样需要DUAL
表。没有 FROM
子句的 SELECT
只返回一行。
@Devio;没错——我提到了 DUAL 语句来说明没有 FROM 时会发生什么;即我的观点是 select 1 a
与 select 1 a from dual
/ 相同,以 Oracle 方式思考它,我的帮助理解发生了什么。
我知道您可以在大多数数据库中运行一个简单的SELECT 3*4
,其中一些数据库(如 Oracle)需要虚拟的DUAL
表。我经常将它与 CTE 一起使用来对值进行硬编码,例如使用 WITH cte AS (SELECT 0.1 AS tax) SELECT id,title,price,price*tax FROM products,cte
。这并没有解释它如何应用于实际引用列的APPLY
。
@Manngo 上述更新是否有助于澄清正在发生的事情;也就是说,这显示了一个场景,您有没有 FROM 语句的子查询,但也没有 APPLY。以上是关于没有 FROM 子句的 OUTER/CROSS APPLY 子查询的主要内容,如果未能解决你的问题,请参考以下文章