在 SQL 子查询中使用多个表进行 Oracle 半联接

Posted

技术标签:

【中文标题】在 SQL 子查询中使用多个表进行 Oracle 半联接【英文标题】:Oracle semi-join with multiple tables in SQL subquery 【发布时间】:2015-06-03 23:37:29 【问题描述】:

这个问题是如何解决在子查询中使用多个表进行半连接时明显的 oracle 限制。我有以下 2 个 UPDATE 语句。

更新 1:

UPDATE
     (SELECT a.flag update_column
      FROM a, b
      WHERE a.id = b.id AND
            EXISTS (SELECT NULL
                    FROM c
                    WHERE c.id2 = b.id2 AND
                          c.time BETWEEN start_in AND end_in) AND
            EXISTS (SELECT NULL
                    FROM TABLE(update_in) d
                    WHERE b.time BETWEEN d.start_time AND d.end_time))
SET update_column = 'F'

执行计划表明这正确执行了 2 个半连接,并且更新在几秒钟内执行。这些需要半连接,因为c.id2 不是b.id2 上的唯一外键,这与b.ida.id 不同。而update_in 根本没有任何约束,因为它是一个数组。

更新 2:

UPDATE
     (SELECT a.flag update_column
      FROM a, b
      WHERE a.id = b.id AND
            EXISTS (SELECT NULL
                    FROM c, TABLE(update_in) d
                    WHERE c.id2 = b.id2 AND
                          c.time > d.time AND
                          b.time BETWEEN d.start_time AND d.end_time))
SET update_column = 'F'

这不做半连接;我相信基于 Oracle 文档,这是因为 EXISTS 子查询中有 2 个表。由于表的大小和分区,此更新需要数小时。但是,除了位于同一行之外,无法将 d.time 与关联的 d.start_timed.end_time 相关联。而我们之所以传入update_in 数组并在这里加入它,是因为在循环中针对每个 time/start_time/end_time 组合运行此查询也证明性能很差。

除了 2 个表之外,还有其他原因导致半连接无法正常工作吗?如果没有,有没有办法绕过这个限制?我缺少一些简单的解决方案,可以在不将 2 个表放入子查询的情况下使这些条件起作用?

【问题讨论】:

我怀疑使用数组。我建议不要尝试将数组视为表,而是创建一个全局临时表(具有适当的索引),用update_in 的值填充它,然后在临时表上进行连接。祝你好运。 包含与update_in 相同值的全局临时表仍需要半联接。 【参考方案1】:

正如 Bob 建议的那样,您可以使用与 update_in 数组具有相同结构的全局临时表 (GTT),但主要区别在于您可以在 GTT 上创建索引,并且如果您使用代表性样本数据填充 GTT,您还可以收集有关表的统计信息,以便 SQL 查询分析器能够更好地预测最佳查询计划。

也就是说,您的两个查询中还有一些其他显着差异:

在第一个查询的第一个存在子句中,您引用了两个没有表引用的列 start_in 和 end_in。我的猜测是它们要么是表 a 或 b 中的列,要么是 sql 语句当前范围内的变量。不清楚是哪个。 在您的第二个查询中,您引用了 d.time 列,但是,您在第一个查询中没有使用该列。

将您的第二个查询更新为以下内容是否会提高其性能?

UPDATE
     (SELECT a.flag update_column
      FROM a, b
      WHERE a.id = b.id AND
            EXISTS (SELECT NULL
                    FROM c, TABLE(update_in) d
                    WHERE c.id2 = b.id2 AND
                          c.time BETWEEN start_in AND end_in AND
                          c.time > d.time AND
                          b.time BETWEEN d.start_time AND d.end_time))
SET update_column = 'F'

【讨论】:

不幸的是,这个问题(以及我问的另一个 SQL 问题)很久以前就使用了一些解决方法解决了,我什至不记得也没有时间发布。但是,澄清一下:在第一个查询中, start_in 和 end_in 是存储过程的输入参数(两个查询中的 update_in 也是如此)。在第二个查询中,d.time 类似于第一个查询的 start_in,除了 update_in 中的每一行需要不同的值,与第一个查询不同,第一个查询它们都相同,因此不需要 update_in 中的列。

以上是关于在 SQL 子查询中使用多个表进行 Oracle 半联接的主要内容,如果未能解决你的问题,请参考以下文章

Oracle(sql)文盲大扫除思维导图系列——多表连接查询子查询

Oracle-查询,,..

在 Oracle 更新语句中使用子查询而不是表名

Oracle SQL:从一个表中选择一个计数,从另一个表中使用子查询缩小选择范围

oracle对表的基本操作

如何在oracle中查询某个表的占用了多大的空间!如果是sql语句请讲的详细子我是菜鸟!