11

postgres 14

I have some table:

CREATE TABLE sometable (
    id integer NOT NULL PRIMARY KEY UNIQUE ,
    a integer NOT NULL  DEFAULT 1,
    b varchar(32) UNIQUE)
PARTITION BY RANGE (id);

But when i try to execute it, i get

ERROR: unique constraint on partitioned table must include all partitioning columns

If i execute same table definition without PARTITION BY RANGE (id) and check indexes, i get:

 tablename    indexname                                   indexdef

 sometable, sometable_b_key, CREATE UNIQUE INDEX sometable_b_key ON public.sometable USING btree (b)
 sometable, sometable_pkey, CREATE UNIQUE INDEX sometable_pkey ON public.sometable USING btree (id)

So... unique constraints exist

whats the problem? how can i fix it?

3 Answers 3

26

On partitioned tables, all primary keys, unique constraints and unique indexes must contain the partition expression. That is because indexes on partitioned tables are implemented by individual indexes on each partition, and there is no way to enforce uniqueness across different indexes.

If you want to use partitioning, you have to sacrifice some consistency guarantees. There is no way around that. What you can do is create unique constraints on the partitions. That will guarantee uniqueness within each partition, but not global uniqueness.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks! But is there a way to constrain column b from duplicates?
No, there isn't.
4

This limitation is also mentioned in the docs

5.11.2.3. Limitations The following limitations apply to partitioned tables:

Unique constraints (and hence primary keys) on partitioned tables must include all the partition key columns. This limitation exists because the individual indexes making up the constraint can only directly enforce uniqueness within their own partitions; therefore, the partition structure itself must guarantee that there are not duplicates in different partitions.

There is no way to create an exclusion constraint spanning the whole partitioned table. It is only possible to put such a constraint on each leaf partition individually. Again, this limitation stems from not being able to enforce cross-partition restrictions.

https://www.postgresql.org/docs/current/ddl-partitioning.html#DDL-PARTITIONING-DECLARATIVE

Comments

1

Although this answer is bit late but for any other person who is facing same issue this might help. I am not very good with writing so please ignore the mistakes.

There is no straight forward approach or any standard way of adding unique index to partition table without partitioned columns as a part of unique index columns. But we have achieved it with a trick.

You can create an index without include your partitioned column so create an index which you want to be part of unique index.

Then you can create a trigger on your table with Insert operation and on that trigger you can validate if your row is already exist with where clause of your unique filter columns without partitioned column and if it exists then throw error.

Below if the trigger function which I have used.

CREATE OR REPLACE FUNCTION public.my_table_trigger() RETURNS trigger LANGUAGE plpgsql AS $function$ begin

if exists (
select
    1
from
    my_table_name mts
where
    mts.column_1= new.column_1
    and mts.column_2 = new.column_2
    and mts.column_3 = new.column_3
    and mts.column_4 = new.column_4
    and mts.column_5 = new.column_5
    and mts.column_6 = new.column_6) then 

    raise exception 'duplicacy error';

else 
    return new;
    
end if;
end;
$function$
;

Cons of this approach This functionality scan every time the table whenever insert query executed.

Impact Minimization But impact will be minimized as index will be created as step 1 for table scan on every insert statement.

Although this approach is just a method to achieve whatever you want but this can lead to slower insert queries. So please analyse your execution cost on your table before use it for production.

1 Comment

The trigger scans the whole table so the optimizer can't use partition pruning, meaning that it must scan N individual "local" indexes, where N is the number of partitions. This is about N times worse than scanning a global index (which PostgreSQL does not have), because the smaller size (height) of each local index does not compensate for the increased number of indexes. This is because N*log(k/N)/log(k) ~ N, where k is the number of key values and k>>N. I guess this is the reason because PostgreSQL developers chose to enforce the rule "unique indexes must include the partition keys".

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.