32

前言

最近,我看到太多极客在评论 Oracle 问题时说“不要使用 (+) 运算符,而是使用 JOIN 语法”。

问题

我确实看到两者都运作良好。但是使用它们之间的真正区别是什么?我会欢迎更多来自经验的答案。

  1. 使用它们时是否与应用程序、性能等方面的限制有关?
  2. 你会给我什么建议?

我确实阅读了有关Oracle 文档的内容,但不足以让我理解或对全面的信息感到满意。

注意:我计划迁移 200 多个包和程序,如果应该使用关键字而不是 (+)

  1. 还有任何免费软件工具可以进行重写吗?

发布样本

┌───────────────────────────────────┬─────────────────────────────────────────────┐
│ INNER JOIN - CONVENTIONAL         │ INNER JOIN - ANSI SYNTAX                    │
├───────────────────────────────────┼─────────────────────────────────────────────┤
│ SELECT                            │ SELECT                                      │
│      emp.deptno                   │       ename,                                │
│ FROM                              │       dname,                                │
│      emp,                         │       emp.deptno,                           │
│      dept                         │       dept.deptno                           │
│ WHERE                             │ FROM                                        │
│      emp.deptno = dept.deptno;    │       scott.emp INNER JOIN scott.dept       │
│                                   │       ON emp.deptno = dept.deptno;          │
├───────────────────────────────────┼─────────────────────────────────────────────┤
│ LEFT OUTER JOIN - CONVENTIONAL    │ LEFT OUTER JOIN - ANSI SYNTAX               │
├───────────────────────────────────┼─────────────────────────────────────────────┤
│ SELECT                            │ SELECT                                      │
│      emp.deptno                   │      ename,                                 │
│ FROM                              │      dname,                                 │
│      emp,                         │      emp.deptno,                            │
│      dept                         │      dept.deptno                            │
│ WHERE                             │ FROM                                        │
│      emp.deptno = dept.deptno(+); │      scott.emp LEFT OUTER JOIN scott.dept   │
│                                   │      ON emp.deptno = dept.deptno;           │
├───────────────────────────────────┼─────────────────────────────────────────────┤
│ RIGHT OUTER JOIN - CONVENTIONAL   │ RIGHT OUTER JOIN - ANSI SYNTAX              │
├───────────────────────────────────┼─────────────────────────────────────────────┤
│ SELECT                            │ SELECT                                      │
│      emp.deptno                   │      ename,                                 │
│ FROM                              │      dname,                                 │
│      emp,                         │      emp.deptno,                            │
│      dept                         │      dept.deptno                            │
│ WHERE                             │ FROM                                        │
│      emp.deptno(+) = dept.deptno; │      scott.emp RIGHT OUTER JOIN scott.dept  │
│                                   │      ON emp.deptno = dept.deptno;           │
├───────────────────────────────────┼─────────────────────────────────────────────┤
│ FULL OUTER JOIN - CONVENTIONAL    │ FULL OUTER JOIN - ANSI SYNTAX               │
├───────────────────────────────────┼─────────────────────────────────────────────┤
│ SELECT                            │ SELECT                                      │
│      *                            │      *                                      │
│ FROM                              │ FROM                                        │
│      emp,                         │      scott.emp FULL OUTER JOIN scott.dept   │
│      dept                         │      ON emp.deptno = dept.deptno;           │
│ WHERE                             │                                             │
│      emp.deptno = dept.deptno(+)  │                                             │
│ UNION ALL                         │                                             │
│ SELECT                            │                                             │
│      *                            │                                             │
│ FROM                              │                                             │
│      emp,                         │                                             │
│      dept                         │                                             │
│ WHERE                             │                                             │
│      emp.deptno(+) = dept.deptno  │                                             │
│      AND emp.deptno IS NULL;      │                                             │
└───────────────────────────────────┴─────────────────────────────────────────────┘

PS:阅读所有分组更新的答案摘要。

4

11 回答 11

25

如果您的 200 多个软件包使用“老式”语法按预期工作,那就顺其自然吧。迁移到 ANSI 语法后,SQL 不会开始更好地执行 - 它只是语法不同。

话虽如此,ANSI 语法更简洁 - 如果您在某些多列外连接中忘记了 (+),您将不会进行正常连接。
过去,ANSI 语法存在一些错误,但如果您使用最新的 11.2 或 12.1,则应该已经修复。
当然,您更了解您的环境和优先级 - 正如 SchmitzIT 所说 - ANSI 语法是 SQL 标准的一部分,并且在使用其他一些 RDBMS 产品时会有所帮助。

于 2013-09-22T13:52:28.810 回答
14

在 11g 中,您应该使用 ANSI 连接语法。它更灵活(支持完全外部连接和分区连接),并且如文档所述:

Oracle 强烈建议您使用前面示例中显示的更灵活的 FROM 子句连接语法。

这个理由就够了。

于 2013-09-19T09:43:07.980 回答
10

将答案分组在一起

  1. 使用显式连接而不是隐式连接(无论它们是否是外连接)是因为使用隐式连接意外创建笛卡尔积要容易得多。使用显式 JOIN,您不能“意外”创建一个。涉及的表越多,您错过一个连接条件的风险就越高。
  2. 与 ANSI 连接相比,基本上 (+) 受到严格限制。此外,它仅在 Oracle 中可用,而所有主要 DBMS 都支持 ANSI 连接语法
  3. 迁移到 ANSI 语法后,SQL 不会开始更好地执行 - 它只是语法不同。
  4. Oracle 强烈建议您使用前面示例中显示的更灵活的 FROM 子句连接语法。过去,ANSI 语法存在一些错误,但如果您使用最新的 11.2 或 12.1,则应该已经修复。
  5. 使用 JOIN 运算符可确保您的 SQL 代码符合 ANSI,从而允许前端应用程序更容易移植到其他数据库平台。
  6. 连接条件对每个表的选择性非常低,而对理论叉积中的元组选择性很高。where 语句中的条件通常具有更高的选择性。
  7. Oracle 在内部将 ANSI 语法转换为 (+) 语法,您可以在执行计划的谓词信息部分看到这种情况。

在 12c 引擎上使用 ANSI 语法的可能陷阱

包括 12c 中 JOIN 中存在错误的可能性。看这里

跟进:

Quest SQL optimizer tool将 SQL 重写为 ANSI 语法。

于 2013-10-01T10:39:28.443 回答
5

除了其他人提到的原因之外,使用 JOIN 运算符可确保您的 SQL 代码符合 ANSI,从而允许前端应用程序更容易地移植到其他数据库平台。

于 2013-09-22T12:35:09.413 回答
4

作为一种实践,您应该始终使用 ANSI 语法。最好不要对包和程序进行返工。相反,您可以在单独对这些脚本进行任何维护时修复问题。由于语法,计划不会有任何差异。

Quest SQL 优化器使用所有可能的组合重写以找到更好的计划。因此,您仍然需要从 100 多个结果中搜索一个 SQL。

于 2014-11-06T16:10:45.340 回答
3

基于选择性分离谓词

将您的查询分成连接条件和 where 条件可以给优化器一个额外的提示。连接条件对每个表的选择性非常低,而对理论叉积中的元组选择性很高。where 语句中的条件通常具有更高的选择性。

在左侧每一行的连接条件中,右侧很可能有一个值(反之亦然)。因此,这样的条件可以很好地连接两个表的结果,但它们对于从结果集中消除每个单独的表中的值并没有太大帮助。

where 子句中的条件通常可用于从结果集中删除一个表中的单个行。

(人类!)优化器的提示

因此,首先在单个表上运行 where 条件并从结果集中消除尽可能多的行是一个很好的策略。之后,可以使用连接条件来组合幸存的行。

不清楚Oracle优化器是否真的使用条件在SQL语句中的位置作为优化查询的提示。我猜它对表统计信息中的硬事实更感兴趣(Oracle 在 11g R1 中处理不同连接的方式发生了一些变化,有关更多详细信息,请参阅Oracle 优化器团队的这篇文章)。

至少对于作为人类的我来说,当我尝试理解和优化查询时,了解语句是否在单个表上具有选择性是非常有帮助的。您还应该考虑这一点,当您想在 ON 子句中放置多个条件(例如ON (a.x=b.x AND a.y=b.y)与将条件之一放在 where 子句中时:只需检查条件的选择性。

结论

对于现有查询,保持语法不变。创建新查询或重构现有查询时,请尝试使用“JOIN ON”语法对选择性谓词进行排序:如果在单个表上不是很可选择,则将其放在 ON 部分,否则放在 WHERE 部分。

于 2013-09-24T15:39:30.910 回答
3

外连接中使用的 (+) 符号是外连接的 Oracle 语法

从可靠的 oracle 来源收集的信息中,我可以看到您可以为现有包保留 Oracle 外连接语法 (+),因为有 200 多个包,并且由于内部 Oracle 仍然将它们从 ANSI 语法转换为 Oracle 语法。

在使用(+)运算符有限制的情况下,请在将来使用 ANSI 语法

请找到有关 (+) 符号外部联接的详细说明的链接,这可能有助于您决定迁移

Oracle 外连接语法和 ANSI 语法

使用 (+) 外连接的限制

有关 Oracle 外连接的更多信息

(+) 使用 Oracle JDBC 驱动程序时,Java 中推荐的外连接运算符

于 2013-09-27T08:21:48.617 回答
2

我在不同的项目中使用了这两种方法,我更喜欢JOIN语法。

  • ON子句中的连接条件和子句中的过滤条件有明确的区分WHERE
  • 使用大量连接更容易阅读和维护大型查询。
于 2013-09-24T16:45:20.683 回答
2

从工作经验来看,使用JOIN而不是(+)更简单,更快,更好看和更好地使用解决方案,特别是当您使用多数据库选择(通过同义词)时,其中有很多表(例如:40 +表)在大数据库中(1000+表,一些表超过20亿行),你会感觉到很大的不同。

于 2013-09-28T17:42:18.597 回答
2

一些评论者说 (+) 语法不允许完全外连接。确实如此,所以这不是问题:(LEFT OUTER JOIN)UNION ALL(RIGHT OUTER JOIN)。

其他人则表示,性能是进行转换的原因。那是一堆废话,尤其是在SQL中。当然,有一些指导方针,但是每个查询和每个数据库都有自己的特殊性,您必须针对特定情况而不是针对一般情况进行调整。

除了不标准之外,从 (+) 切换的原因是它的局限性,而不是新的显式语法:http ://docs.oracle.com/cd/E16655_01/server.121/e17209/queries006.htm#SQLRF52354 . 从这里开始阅读:“Oracle 建议您使用 FROM 子句 OUTER JOIN 语法而不是 Oracle 连接运算符。”

于 2013-09-29T07:11:12.673 回答
1

直到上周,我一直是 ANSI 加入的倡导者。直到在我的一个选择中意识到奇怪的行为。在挖掘了 Oracle 错误库后,我了解到 Oracle 本身不支持 ANSI 连接 - ANSI 连接被转换为 (+) 表示法,然后进行处理。而且这个翻译有一些不可接受的错误。这是不可接受的,因为我们的客户没有应用补丁,而且即使在 12c 版本中也引入了一些新错误 - 非常简单的测试用例,包含三个表、一个记录和两个外部连接。似乎人们根本没有进行自动化回归测试

于 2019-11-04T17:22:26.423 回答