多对多的独特性

时间:2012-11-01 14:59:54

标签: sql postgresql many-to-many

我无法弄清楚谷歌的条款,所以帮助标记这个问题或者只是指出我对相关问题的看法会有所帮助。

我相信我有一个典型的多对多关系:

CREATE TABLE groups (
  id integer PRIMARY KEY);

CREATE TABLE elements (
  id integer PRIMARY KEY);

CREATE TABLE groups_elements (
  groups_id integer REFERENCES groups,
  elements_id integer REFERENCES elements,
  PRIMARY KEY (groups_id, elements_id));

我想要一个约束条件,即给定的一组elements_ids只能有一个groups_id。

例如,以下内容有效:

groups_id | elements_id
        1 | 1
        1 | 2
        2 | 2
        2 | 3

以下内容无效,因为第1组和第2组相同。

groups_id | elements_id
        1 | 1
        1 | 2
        2 | 2
        2 | 1

并非每个元素子集都必须具有一个组(这不是幂集),但可能会形成新的子集。我怀疑我的设计不正确,因为我真的在谈论将一个组添加为单个实体。

如何为元素子集创建标识符而不存在重复子集的风险?

3 个答案:

答案 0 :(得分:1)

这是一个有趣的问题。

一个解决方案,尽管是一个笨重的解决方案,将在groups表中存储groups_id和elements_id的串联:1-1-2并使其成为唯一索引。

尝试在插入新行之前搜索重复的组,将会带来巨大的性能损失。

答案 1 :(得分:0)

以下查询会吐出有问题的组ID:

with group_elements_arr as (
    select groups_id, array_agg(elements_id order by elements_id) elements 
    from group_elements 
    group by groups_id )
select elements, count(*), array_agg(groups_id) offending_groups 
    from group_elements_arr 
    group by elements 
    having count(*) > 1;

根据group_elements的大小及其变化率,您可能会将这些内容填充到一个触发器中观察group_elements。如果速度不够快,您可以将group_elements_arr转化为触发器管理的真实表格。

我认为,触发器应为FOR EACH STATEMENTINITIALLY DEFERRED,以便轻松建立新群组。

答案 2 :(得分:0)

来自用户ypercube的此链接最有帮助:unique constraint on a set。简而言之,每个人所说的都是正确的。

这是一个权衡问题,但这里有最好的选择:

a)将一个哈希值或其他元素值组合添加到groups表中并使其唯一,然后使用触发器填充groups_elements表。这种方法的优点在于,只要您拒绝对groups_elements进行裸露更新,它就会保留查询能力并强制执行约束。遗憾的是,它增加了复杂性,现在您已经在数据库中引入了“如何唯一地表示一组元素”等逻辑。

b)按原样保留表,并控制对访问层的groups_elements访问,无论是存储过程还是其他方式。这样做的好处是可以保留查询能力并保持数据库本身的简单性。但是,这意味着您正在将分析约束移动到访问层中,这必然意味着您的访问层将需要更复杂。另一点是它将数据与数据本身分开,这既有利也有弊。如果您需要更快地访问某个集合是否已存在,您可以单独解决该问题。