Você pode usar uma matriz para a coluna e um operador "está contido por" para a restrição CHECK:
create table pancakes (
color varchar(10)[] not null,
check (color <@ ARRAY['red', 'green', 'blue']::varchar[])
);
E então coisas assim acontecem:
=> insert into pancakes values (ARRAY['red']);
INSERT 0 1
=> insert into pancakes values (ARRAY['red','green','blue']);
INSERT 0 1
=> insert into pancakes values (ARRAY['red','green','blue','black']);
ERROR: new row for relation "pancakes" violates check constraint "pancakes_color_check"
=> select * from pancakes;
color
------------------
{red}
{red,green,blue}
(2 rows)
Isso permitirá
{red,red}
na coluna embora; se não permitir {red,red}
é importante, então você pode adicionar uma função para verificar valores de cores exclusivos na matriz e ajustar a restrição CHECK:create function has_unique_colors(varchar[]) returns boolean as $$
select (select count(distinct c) from unnest($1) as dt(c)) = array_length($1, 1);
$$ language sql;
create table pancakes (
color varchar(10)[] not null,
check (color <@ ARRAY['red', 'green', 'blue']::varchar[] and has_unique_colors(color))
);
Outra opção seria uma pilha de tabelas de associação com valores escalares simples nas colunas. No entanto, isso pode ser complicado se você tiver seis dessas colunas. Você também pode usar a versão da função de Erwin se precisar se preocupar com NULLs nos "sets":
create function has_unique_colors(varchar[]) returns boolean as $$
select not exists(select c from unnest($1) dt(c) group by 1 having count(*) > 1);
$$ language sql;