在WHERE子查询中看不到FROM子查询的原因是什么?

Alw*_*ing 1 sql oracle

考虑:

SELECT DISTINCT student_id
FROM (SELECT  * FROM Grades WHERE dept_id = 'MT') T
WHERE grade = (SELECT MAX(grade) FROM T);
Run Code Online (Sandbox Code Playgroud)

Oracle抱怨T在WHERE中的子查询中没有现有的表.我知道我可以通过使用轻松解决这个问题WITH,但我仍然想了解.管理此案例的SQL规则是什么?该规则背后的逻辑是什么?

Mat*_*lie 8

我不确定原因是否重要; 它是,并且知道为什么不改变它.

由于SQL是声明性的,因此存在关于范围,执行顺序,优先顺序等的规则.这些规则使基于成本的计划程序能够生成将实际执行的计划.一个这样的规则是您无法在同一组上评估两个独立查询.即使T是一个材料表,引用它两次也会将它作为两个独立的集合引入计划中.

相反,您需要一种不同的方式来表达您的要求,这更符合语言.一个你不试图多次解析同一个集合的地方.

例如,您可以通过这种方式从同一表达式中获取两个集合 ...

WITH
  T AS
(
  SELECT * FROM Grades WHERE dept_id = 'MT'
)
SELECT DISTINCT student_id
FROM T
WHERE grade = (SELECT MAX(grade) FROM T);
Run Code Online (Sandbox Code Playgroud)

或者,您可以使用窗口函数并允许内部引擎确定以最低成本评估所有术语的最佳方式......

SELECT
  *
FROM
(
  SELECT
    Grades.*,
    MAX(grade) OVER ()   AS max_grade
  FROM
    Grades
  WHERE
    dept_id = 'MT'
)
  T
WHERE
  grade = max_grade
Run Code Online (Sandbox Code Playgroud)



非常长的编辑:反对该提案的主观和客观论点


建议外部查询中定义的集合可用作内部查询中的独立集合.

SELECT DISTINCT
  student_id
FROM
(
  SELECT  * FROM Grades WHERE dept_id = 'MT'
)
  newSetDefinition
WHERE
  grade = (SELECT MAX(grade) FROM newSetDefinition)

-----------------------------
Functionally Equivalent To...
-----------------------------

WITH
  newSetDefinition
AS
(
  SELECT  * FROM Grades WHERE dept_id = 'MT'
)
SELECT DISTINCT
  student_id
FROM
  newSetDefinition
WHERE
  grade = (SELECT MAX(grade) FROM newSetDefinition)
Run Code Online (Sandbox Code Playgroud)


这意味着以下内容也应该有效......

SELECT DISTINCT
  newSetDefinition.student_id
FROM
(
  SELECT  * FROM Grades WHERE dept_id = 'MT'
)
  newSetDefinition
INNER JOIN
(
  SELECT MAX(grade) AS maxGrade FROM newSetDefinition
)
  newSetSummary
    ON newSetSummary.maxGrade = newSetDefinition.grade

-----------------------------
Functionally Equivalent To...
-----------------------------

WITH
  newSetDefinition
AS
(
  SELECT  * FROM Grades WHERE dept_id = 'MT'
)
SELECT DISTINCT
  newSetDefinition.student_id
FROM
  newSetDefinition
INNER JOIN
(
  SELECT MAX(grade) AS maxGrade FROM newSetDefinition
)
  newSetSummary
    ON newSetSummary.maxGrade = newSetDefinition.grade
Run Code Online (Sandbox Code Playgroud)

到现在为止还挺好...


对于嵌套查询,它变得有点模糊,因为由于不同的范围可用性和命名冲突,以下不可能准确地表示CTE.有必要在子查询中定义CTE ......

SELECT
  *
FROM
(
  SELECT * FROM table WHERE something = 1
)
  smeg
INNER JOIN
(
  SELECT
    *
  FROM
  (
    SELECT * FROM table WHERE somethingElse = 2
  )
    smeg
  INNER JOIN
  (
    SELECT MAX(id) AS maxID FROM smeg
  )
    smegSummary
      ON smegSummary.maxID = smeg.ID
)
  smegSubSet
    ON smegSubSet.parentID = smeg.ID

-----------------------------
Functionally Equivalent To...
-----------------------------

WITH
   smeg AS
(
  SELECT * FROM table WHERE something = 1
)
SELECT
  *
FROM
  smeg
INNER JOIN
(
  WITH
    smeg AS
  (
    SELECT * FROM table WHERE something = 1
  )
  SELECT
    *
  FROM
    smeg
  INNER JOIN
  (
    SELECT MAX(id) AS maxID FROM smeg
  )
    smegSummary
      ON smegSummary.maxID = smeg.ID
)
  smegSubSet
    ON smegSubSet.parentID = smeg.ID
Run Code Online (Sandbox Code Playgroud)

好的,没关系,有点不整洁.CTE有助于避免需要深度嵌套,因此使用CTE的嵌套语法是"混乱的",但即使这是一个主观的度量.

当您看到"set reference"时,您会查看外部查询,直到找到具有该别名的集合,如果找不到,则使用常规规则; CTES,然后是当前模式中的表/视图,然后是当前数据库中的表/视图,但是不同的模式,都考虑了权限等.

精细,相当标准的范围规则.


但是下一个场景更客观存在问题......

SELECT
  *
FROM
(
  SELECT * FROM smeg WHERE something = 1
)
  smeg
Run Code Online (Sandbox Code Playgroud)

在当前的ANSI-SQL中,如果有一个带有名称的表,这很好smeg.

在AlwaysLearning-SQL中,它是一个循环引用."最近"的定义smeg是外部查询."覆盖"任何名为的表或视图smeg.所以,内部查询现在从......本身中选择......

有一种说法是"只是让它引发循环引用错误".

但这打破了向后兼容性.

想象一下,如果Oracle将此功能添加到v13?以前突然发生的查询开始引发循环引用错误?为什么?为了使一些子查询像CTE一样工作,假设这样做有帮助/方便吗? To make some aspects of life "convenient" we broke some of your queries.

打破向后兼容性.但是,只有当收益远远大于后果.

在这种情况下,任何可以通过您的建议完成的事情都可以通过CTE完成.并且添加了CTE而没有破坏任何遗留行为.并且(主观地/可论地) CTE可以以更结构化,更易维护,更易于阅读,更易于调试等方式执行此操作.

我个人非常高兴,没有人打破一些查询来实现一些非常小众的功能.