我遇到一个问题,我必须从表('CHILD')中选择应该有另一个表('PARENT')的外键的行。问题是外键被破坏(长话短说,表被分区,并且由于某种原因有孤儿),我必须在恢复外键之前清理子表。我想做的是(大致):
SELECT child.ID
from CHILD child
WHERE child.PARENT_ID NOT IN
(
SELECT parent.ID FROM PARENT parent
);
这似乎是正确的(根据结果判断),但效率很低:有1M个结果,子表包含100M +行。
因为我必须删除来自该查询结果的每一行,所以我使用分页,但是这意味着每次都会重复NOT IN查询。
出于这个原因,我想知道是否有任何方法可以提高查询的性能。我已经尝试加入表格,但我已经意识到它不起作用,因为我应该加入child.PARENT_ID = parent.ID
,所以没有结果。
所以问题是:有没有办法重写NOT IN查询,以便它更高效?
答案 0 :(得分:2)
每次不重复不在查询中。查询优化器可能会进行各种优化。然后,对于这样的查询,它可以决定扫描每一行,而不管索引,纯粹基于两个表之间的行数之间的平衡。
您也可以将它写为LEFT JOIN
,如下所示,但它可能更难以阅读,因为它不能很好地传达意图(尽管这本身并不坏,因为这是一次性的工作只要)。可能的情况是优化器只是将其视为同一个查询。
SELECT child.ID
FROM CHILD child
LEFT JOIN PARENT parent ON parent.ID = child.PARENT_ID
WHERE parent.ID IS NULL
第三种语法是使用NOT EXISTS
。
SELECT child.ID
FROM CHILD child
WHERE NOT EXISTS (
SELECT *
FROM PARENT parent
WHERE parent.ID = child.PARENT_ID
)
答案 1 :(得分:0)
这种语法通常效率更高:
select id from child
where
parent_id in
(
select parent_id from child
minus
select id from parent
);
现在,当您删除子行时,这将非常慢。
相反,您可以更快地构建要保留的子记录的新表,然后重命名或删除旧的子表(先备份!)并将新的子表重命名为child。
如果您随后创建主键和外键约束,则不会再有任何损坏的链接。
我也担心你会提到分页。我不确定你的意思,但听起来相当简单,一百万行不可行。
答案 2 :(得分:0)
在这种情况下,我认为没有必要考虑空值。如果将外键约束应用于子parent_id列,则父对应列上应该存在主键或唯一约束。
如果child允许parent_id中的空值,您可以:
update child
set parent_id = null
where parent_id in (select parent_id from child
minus
select id from parent);
在这种情况下,我们希望使用减去结果集中应该有少量的孤儿键。
如果parent_id具有非空约束,则使用相同的语法删除行
delete child
where parent_id in (select parent_id from child
minus
select id from parent);
在对子表进行任何更改之前备份孤儿可能是个好主意。
create table child_orphans as
select *
from child
natural join (select parent_id from child
minus
select id from parent);
这是有效的,因为从减号操作得到的结果很小,而且因为使用了内连接而不是半连接(in)。如果优化器不会干扰太多,则情况确实如此。