我正在使用如下查询执行更新:
UPDATE (SELECT h.m_id,
m.id
FROM h
INNER JOIN m
ON h.foo = m.foo)
SET m_id = id
WHERE m_id IS NULL
一些信息:
h
约为500万行h
中的所有行都包含NULL
m_id
个值
m
约为50万行m_id
上的h
是指向表id
上的m
的索引外键id
上的m
是主键m.foo
和h.foo
此查询的EXPLAIN PLAN
表示散列连接和全表扫描,但我不是DBA,所以我无法真正解释它。
查询本身运行了几个小时但没有完成。我原本预计它会在不超过几分钟内完成。我还尝试了以下查询重写:
UPDATE h
SET m_id = (SELECT id
FROM m
WHERE m.foo = h.foo)
WHERE m_id IS NULL
这个提到的ROWID查找和索引使用的EXPLAIN PLAN
,但是它也持续了几个小时没有完成。我也一直认为像这样的查询会导致子查询被外部查询的谓词的每个结果执行,所以我认为这个重写的表现非常差。
我的方法有什么问题,还是我的问题与索引,表空间或其他一些与查询无关的因素有关?
修改
我也有这样的简单计数查询的糟糕表现:
SELECT COUNT(*)
FROM h
WHERE m_id IS NULL
这些查询需要大约30秒到有时大约30分钟(!)。
我注意到没有锁,但这些表的表空间现在只占99.5%的使用率(仅约6MB免费)。我被告知,只要索引被使用,这无关紧要,但我不知道......
答案 0 :(得分:3)
有些观点:
Oracle执行不索引NULL
值(它将索引作为全局非null元组的一部分的NULL
,但这就是它)。
由于HASH JOIN
和h
的大小,Oracle需要m
。这可能是表现最好的选择。
第二个UPDATE
可能让Oracle使用索引,但是Oracle通常很聪明地合并子查询。无论如何,这将是一个更糟糕的计划。
您是否有最新的,合理的架构统计信息? Oracle 确实需要体面的统计数据。
在执行计划中,这是HASH JOIN
中的第一个表?为获得最佳性能,它应该是较小的表(在您的情况下为m
)。如果您没有良好的基数统计数据,Oracle将会搞砸。您可以强制Oracle使用cardinality
提示假定固定的基数,这可能有助于Oracle获得更好的计划。
例如,在您的第一个查询中:
UPDATE (SELECT /*+ cardinality(h 5000000) cardinality(m 500000) */
h.m_id, m.id
FROM h
INNER JOIN m
ON h.foo = m.foo)
SET m_id = id
WHERE m_id IS NULL
SELECT COUNT(*)
消耗了30多秒,因为有问题的表有2.5亿个已删除的行。如果是这种情况,我建议用DBA分析您的具体情况,这样他/她就可以从已删除的行中回收空间并降低高水位线。答案 1 :(得分:2)
据我记忆,WHERE m_id IS NULL
执行全表扫描,因为无法对NULL值编制索引。
全表扫描意味着引擎需要读取表中的每条记录以评估WHERE条件,并且不能使用索引。
如果m_id IS NULL
,您可以尝试将virtual column集添加到非空值,并将此列编入索引,并在WHERE条件中使用此列。
然后你也可以将WHERE条件从UPDATE语句移动到子选择,这可能会使语句更快。
由于JOIN很昂贵,请将INNER JOIN m ON h.foo = m.foo
重写为
WHERE h.foo IN (SELECT m.foo FROM m WHERE m.foo IS NOT NULL)
也可以提供帮助。
答案 2 :(得分:1)
对于大型表,MERGE通常比UPDATE快得多。试试这个(未经测试):
MERGE INTO h USING
(SELECT h.h_id,
m.id as new_m_id
FROM h
INNER JOIN m
ON h.foo = m.foo
WHERE h.m_id IS NULL
) new_data
ON (h.h_id = new_data.h_id)
WHEN MATCHED THEN
UPDATE SET h.m_id = new_data.new_m_id;
答案 3 :(得分:0)
我会在迭代中更新表格,例如,根据where h.date_created > sysdate-30
添加条件,在完成后我会运行相同的查询并将条件更改为:where h.date_created between sysdate-30 and sysdate-60
等等。如果你不喜欢没有像date_created
这样的列可能还有另一个可以过滤的列吗?例如:WHERE m.foo = h.foo AND m.foo between 1 and 10
只有plan
的结果可以解释为什么这次更新的成本很高,但是一个有根据的猜测是两个表都非常大,并且有很多NULL
个值以及很多匹配(m.foo = h.foo
)......
答案 4 :(得分:0)
尝试无证件提示/ * + BYPASS_UJVC * /。如果可行,请在m.foo上添加UNIQUE / PK约束。