【问题标题】:Oracle semi-join with multiple tables in SQL subquery在 SQL 子查询中使用多个表进行 Oracle 半联接
【发布时间】: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 相同值的全局临时表仍需要半联接。

标签: sql oracle semi-join


【解决方案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 中的列。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-29
  • 2023-01-20
  • 1970-01-01
  • 2021-03-16
相关资源
最近更新 更多