对一列的唯一约束,在其他列中排除具有相同值的行

时间:2012-02-16 12:03:18

标签: sql postgresql

我想在列value中添加唯一键,但我必须忽略列valueheader_id中具有相同值的行。例如,请考虑此表:

id | header_id | value
 1 |         1 | a
 2 |         1 | a
 3 |         2 | a

因此,第1行和第2行指向同一个对象,唯一键应该接受它们,但第3行具有不同的header_id(指向另一个对象),因为它具有与value相同的value对象1,它应该违反唯一约束并引发错误。

编辑16.2:1327:
我使用核心框架生成列来处理历史记录,因此我无法规范化表格。我的课程有很多专栏,但在本例中我只考虑{{1}}列。

3 个答案:

答案 0 :(得分:3)

如果您可以稍微更改表格结构,则可以这样做:

your_table
id   header_value
1    1
2    1
3    2

header_value
id   header_id value
1    1         a
2    2         a

your_table.header_value的外键约束添加到header_value.id

现在,您可以在header_value.value上添加唯一约束。

答案 1 :(得分:2)

您可以使用触发器模拟具有所需属性的唯一约束。像这样的东西可以解决这个问题:

create or replace function sort_of_unique() returns trigger as $$
declare
    got_one boolean;
begin
    select exists(
        select 1
        from your_table
        where header_id != new.header_id
          and value      = new.value
    ) into got_one;
    if got_one then
        raise exception 'Uniqueness violation in your_table';
    end if;
    return new;
end;
$$ language plpgsql;

create trigger sort_of_unique_trigger
before insert or update on your_table
for each row execute procedure sort_of_unique();

然后你就会发生这样的事情:

=> insert into your_table (id, header_id, value) values (1, 1, 'a');
=> insert into your_table (id, header_id, value) values (2, 1, 'a');
=> insert into your_table (id, header_id, value) values (3, 2, 'a');
ERROR:  Uniqueness violation in your_table
=> insert into your_table (id, header_id, value) values (3, 2, 'b');
=> update your_table set value = 'a' where id = 3;
ERROR:  Uniqueness violation in your_table

您可以通过将WHERE子句附加到索引来创建partial unique indexes。这允许您将唯一性约束应用于表的切片;但是,我无法想办法让WHERE子句指定一个"反切片"所以我没有看到一种方法来使用部分索引来完成这项工作。我可能会遗漏一些明显的东西。

答案 2 :(得分:2)

过了一会儿我发现了什么。使用约束CHECK with function来确定是否存在(不能在CHECK语句中使用SELECT,但可以使用具有所需select的函数)

CREATE OR REPLACE FUNCTION is_value_free(_header_id integer, _value varchar) RETURNS BOOLEAN AS 
$$
  BEGIN
    RETURN NOT EXISTS (SELECT header_id,value FROM myschema.mytalbe WHERE value LIKE _value AND header_id != _header_id LIMIT 1);
  END;
$$ LANGUAGE plpgsql;

ALTER TABLE mytable ADD CONSTRAINT uniq_value CHECK (is_value_free(header_id,value))