反向外键级联(或如何收集数据库垃圾)

时间:2017-10-19 18:47:03

标签: sql oracle11g foreign-keys

这类似于this question,虽然有一个扭曲(即,我需要的是基本上这里的引用计数行为)。

我们有几个表(Foo,Bar,Baz),其中存储的东西都是零或更多的另一种东西(Blah)。我们有时会制作新的Blah并将它们贴在Foo,Bar或Baz上;但是,我们不会编辑现有的Blahs,因为它们可能会被多个事物同时指向。目前,我们有一个批处理过程,扫描数据库中的所有Foos,Bars和Bazes,标记Blahs,然后删除所有未标记的Blahs,但这是非常昂贵的,所以我们正在寻找一种方法它在线,理想情况下通过数据库本身。我们可以研究基于触发器的方法,但宁愿将它们视为最后的手段。

具体而言,请参考this SQLFiddle中的架构和数据:

  • 如果我插入一个新栏'some_bar'并让它指向一个带有PK'r'的新Blah,则只应删除已经孤立的Blah'x'。
  • 如果我删除'a_foo',Blahs'a'和'x'应该是唯一被删除的 - 'b'和'c'特别需要留下来
  • 如果我删除'another_foo',则需要删除Blahs'x'和'y'
  • 如果我删除'a_bar',只能删除Blah'x' - 特别需要留下Blah'c'
  • 如果我再次删除'another_bar',只能删除Blah'x' - Blah'q'需要留下来
  • 如果我删除'a_baz',则需要删除所有Blahs'p','x'和'z'
  • 如果我删除'another_baz',只能删除Blah'x' - Blahs'b'和'q'需要留下来

我是否坚持使用触发器或现有的标记扫描批处理来完成这项工作,或者有更好的方法吗?

1 个答案:

答案 0 :(得分:1)

不需要标记和扫描批处理,因为只要BLAHS.PK永远不能包含逗号,以下查询就可以完成工作:

delete from blahs 
where not exists (select 1 from foo where ','||some_blahs||',' like '%,'||blahs.pk||',%')
  and not exists (select 1 from bar where bar.blah = blahs.pk)
  and not exists (select 1 from baz where blahs.pk in (baz.a_blah, baz.another_blah));

您的表格FOO很麻烦,因为您无法像BARBAZ一样定义任何参照完整性或任何有用的索引。

更好的数据库设计可能是用多对多关系表替换FOO.SOME_BLAHS

create table foo_blahs ( foo_pk varchar2(20)  references foo on delete cascade
                       , blah_pk varchar2(20) references blahs
                       , constraint foo_blah_pk primary key (foo_pk, blah_pk) enable);

然后代替:

insert into foo (pk, some_blahs) values ('a_foo', 'a,b,c');

你会使用:

insert into foo (pk) values ('a_foo');
insert into foo_blahs (foo_pk, blah_pk) values ('a_foo', 'a');
insert into foo_blahs (foo_pk, blah_pk) values ('a_foo', 'b');
insert into foo_blahs (foo_pk, blah_pk) values ('a_foo', 'c');

,删除查询将变为:

delete from blahs 
where not exists (select 1 from foo_blahs where foo_blahs.blah_pk = blahs.pk)
  and not exists (select 1 from bar where bar.blah = blahs.pk)
  and not exists (select 1 from baz where blahs.pk in (baz.a_blah, baz.another_blah));