2012-02-16 84 views
3

我想向列value添加一个唯一键,但我必须忽略列valueheader_id中具有相同值的行。例如,考虑这个表:对一列的唯一约束,排除具有相同值的行的其他

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

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

编辑16.2:1327:
我使用的是核心框架,其产生的列来处理的历史,所以我不能正常化的表。我的班有很多列,但对于这个例子,我只考虑value列。

回答

2

过了一段时间,我发现了一些东西。使用约束CHECK和函数来确定是否存在(不能在CHECK语句中使用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)) 
+0

这是一个合理的方法。你也可以使用'select exists'(从表中选择1 ...))'而不是'limit',这个习惯用法通常被优化器识别。 – 2012-02-17 16:59:35

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_valueheader_value.id

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

+1

+1规范化。你可能根本不需要'header_value.id'。 – 2012-02-16 12:16:17

+0

我无法更改我的表,列标识为header_id,其他标识由核心框架生成以处理对象的历史记录。我的课只有列值(我的意思是Java中的类)。但是,谢谢 – Perlos 2012-02-16 12:24:47

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子句来指定一个“反切片”,所以我没有看到一种方法来使这部分索引工作。我可能会错过一些明显的事情。

+0

+1对于部分唯一索引...我不知道那些存在 – Andre 2012-02-16 19:58:31

+0

带触发器的想法是很好的。我如此尝试了部分唯一索引,但不知道如何编写它。它会抛出错误,当想要使用选择在哪里clausule,所以认为它只是检查插入行中的值。 – Perlos 2012-02-17 10:05:03