考虑:
SELECT DISTINCT student_id
FROM (SELECT * FROM Grades WHERE dept_id = 'MT') T
WHERE grade = (SELECT MAX(grade) FROM T);
Oracle抱怨WHERE中子查询中的T
不是现有表。我知道我可以使用WITH
轻松解决这个问题,但我仍然想了解。管理此案例的SQL规则是什么?该规则背后的逻辑是什么?
答案 0 :(得分:8)
我不确定原因是否重要;它是,并且知道为什么不改变它。
由于SQL是声明性的,因此存在关于范围,执行顺序,优先顺序等的规则。这些规则使基于成本的计划程序能够生成将实际执行的计划。一个这样的规则是您无法在同一组上评估两个独立查询。即使T
是一个材料表,引用它两次也会将它作为两个独立的集合引入计划中。
相反,您需要一种不同的方式来表达您的要求,这更符合语言。一个你不试图多次解析同一个集合的地方。
例如,您可以通过这种方式从相同的 表达式 中获取两个 集 ... < / p>
WITH
T AS
(
SELECT * FROM Grades WHERE dept_id = 'MT'
)
SELECT DISTINCT student_id
FROM T
WHERE grade = (SELECT MAX(grade) FROM T);
或者,您可以使用窗口函数并允许内部引擎确定以最低成本评估所有术语的最佳方式......
SELECT
*
FROM
(
SELECT
Grades.*,
MAX(grade) OVER () AS max_grade
FROM
Grades
WHERE
dept_id = 'MT'
)
T
WHERE
grade = max_grade
非常长的编辑: 反对该提案的主观和客观论点
建议外部查询中定义的集合可用作内部查询中的独立集合。
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)
这意味着以下内容也应该有效......
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
到目前为止,非常好......
使用嵌套查询会变得有点模糊,因为由于不同的范围可用性和命名冲突,以下内容无法准确表示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
好的,这没关系,有点不整洁。 CTE有助于避免需要深度嵌套,因此对CTE使用嵌套语法是“混乱的”,但即使这是一个主观测量。
当您看到“set reference”时,您会查看外部查询,直到找到具有该别名的集合,如果找不到,则使用常规规则; CTES,然后是当前模式中的表/视图,然后是当前数据库中的表/视图,但不同的模式,都考虑了权限等。
精细,相当标准的范围规则。
但是下一个场景更客观地存在问题...
SELECT
*
FROM
(
SELECT * FROM smeg WHERE something = 1
)
smeg
在当前的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可以通过更结构化,更易维护,更易于阅读,更易于调试等方式实现这一目标。
我个人非常高兴没有人打破一些问题来实现一些非常小众的功能。