oracle反加入替代品

时间:2017-12-28 08:19:00

标签: sql oracle anti-join

我遇到一个问题,我必须从表('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查询,以便它更高效?

3 个答案:

答案 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列,则父对应列上应该存在主键或唯一约束。

  • 如果应用主键约束,则不是空值 允许。
  • 使用唯一约束时,行为null 值不会被视为外来引用的唯一值 键。
  • 如果子parent_id为null,则将忽略外键检查。如果我们想要一个强关系,我们应该在子parent_id上应用NOT NULL约束。

如果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)。如果优化器不会干扰太多,则情况确实如此。