【问题标题】:OUTER/CROSS APPLY Subquery without FROM clause没有 FROM 子句的 OUTER/CROSS APPLY 子查询
【发布时间】: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?" 你不能自己运行一下看看发生了什么吗?
  • 没有FROMSELECT 就像从一个虚构的单行表中选择一样。在APPLY 的情况下,如果您引用主表中的列,那么它只会使用外部查询中当前行的值作为相关参数。
  • 我注意到 1 票反对,1 票结束此问题。有人愿意解释一下,还是这只是一个肇事逃逸?

标签: sql sql-server cross-apply


【解决方案1】:

首先考虑

SELECT o.name, o.type
FROM sys.objects o

现在考虑

SELECT o.name, (SELECT o.type) AS type
FROM sys.objects o

没有FROMSELECT 就像从虚构的单行表中进行选择一样。上述内容不会改变结果,标量子查询只是充当相关子查询并使用外部查询的值。

APPLY 的行为方式相同。对来自外部查询的列的引用只是作为相关参数传入。所以这是一样的

SELECT o.name, ca.type
FROM sys.objects o
CROSS APPLY (SELECT o.type) AS ca

APPLY 通常比SELECT 中的标量子查询更有能力(因为它可以扩展一行或从结果中删除行)

【讨论】:

  • 好的,这给我带来了一个新的查询,为什么 apply 会这样工作,为什么每个子查询都不能像那样进行
【解决方案2】:

你提到的不是SUBQUERY。它是单独的表表达式。在正确的表达式中使用 FROM 子句是否有问题。

  1. 如果您在右表表达式中使用 FROM 子句,那么您就有了右表表达式中数据的来源。
  2. 如果在右表达式中不使用 FROM 子句,则数据源来自左表表达式。

首先我们来看看什么是APPLY操作符。 Reference BOL

使用应用

APPLY 运算符的左右操作数都是表 表达式。 这些操作数之间的主要区别在于 right_table_source 可以使用接受一列的表值函数 来自 left_table_source 作为函数的参数之一。 left_table_source 可以包含表值函数,但它 不能包含来自 right_table_source 的列的参数。

APPLY 运算符按以下方式工作以生成表 FROM 子句的来源:

  1. 根据 left_table_source 的每一行评估 right_table_source 以生成行集。

    right_table_source 中的值取决于 left_table_source。 right_table_source 可以大致这样表示: TVF(left_table_source.row),其中 TVF 是一个表值函数。

  2. 将在评估 right_table_source 和 left_table_source 时为每一行生成的结果集 执行 UNION ALL 操作。

    APPLY 运算符的结果产生的列列表是 left_table_source 中与 right_table_source 中的列列表。

根据您使用 APPLY 运算符的方式,它将表现为相关子查询或 CROSS JOIN

  1. 在右表表达式中使用左表表达式的值
--  without FROM (similar to Correlated Subquery)
    SELECT id, data, value
    FROM test OUTER APPLY(SELECT data*10 AS value) AS sq;
  1. 不在右表表达式中使用左表表达式的值
--  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 aselect 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。
猜你喜欢
  • 1970-01-01
  • 2013-08-16
  • 2011-10-07
  • 1970-01-01
  • 1970-01-01
  • 2021-08-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多