postgresql分区的唯一索引

时间:2012-09-06 11:15:31

标签: postgresql unique unique-constraint

我把表叫做cdrs:

CREATE TABLE cdrs (
    i_cdr bigint NOT NULL,
    i_cdrs_connection bigint NOT NULL,
    i_call bigint NOT NULL,
    customer_name character varying(156) NOT NULL,
    client_name_id character varying(256) NOT NULL,
    connection_name character varying(156) NOT NULL,
    vendor_name_id character varying(256) NOT NULL,    
    setup_time timestamp with time zone NOT NULL,
    c_result_id bigint NOT NULL,
    v_result_id bigint NOT NULL
    );

现在使用依赖继承来创建分区,插入到父表并执行更新。此函数尝试插入,如果需要分区不存在,则使用索引创建它。我们在i_cdrs_connection上为每个分区创建了唯一索引,也在父级上创建了。

CREATE UNIQUE INDEX i_cdrs_connection ON cdrs(i_cdrs_connection)

CREATE OR REPLACE FUNCTION cdrs_insert_trigger() RETURNS TRIGGER AS $$
BEGIN
    EXECUTE 'INSERT INTO cdrs_'|| to_char(NEW.setup_time, 'YYYY_MM_DD') ||' SELECT ($1).*'
    USING NEW;
    RETURN NULL;
    EXCEPTION
        WHEN undefined_table THEN
            EXECUTE 'CREATE TABLE IF NOT EXISTS cdrs_'|| to_char(NEW.setup_time, 'YYYY_MM_DD') ||' (CHECK ( setup_time >= '''|| to_char(NEW.setup_time, 'YYYY-MM-DD 00:00') ||''' AND setup_time < '''|| to_char(NEW.setup_time + INTERVAL '1 day', 'YYYY-MM-DD 00:00') ||''' )) INHERITS (cdrs)';
            EXECUTE 'CREATE UNIQUE INDEX i_cdrs_connection_'|| to_char(NEW.setup_time, 'YYYY_MM_DD') ||' ON cdrs_'|| to_char(NEW.setup_time, 'YYYY_MM_DD') ||' (i_cdrs_connection)';
            EXECUTE 'CREATE INDEX i_cdr_'|| to_char(NEW.setup_time, 'YYYY_MM_DD') ||' ON cdrs_'|| to_char(NEW.setup_time, 'YYYY_MM_DD') ||' (i_cdr)';
            EXECUTE 'CREATE INDEX i_call_'|| to_char(NEW.setup_time, 'YYYY_MM_DD') ||' ON cdrs_'|| to_char(NEW.setup_time, 'YYYY_MM_DD') ||' (i_call)';
            EXECUTE 'CREATE INDEX setup_time_'|| to_char(NEW.setup_time, 'YYYY_MM_DD') ||' ON cdrs_'|| to_char(NEW.setup_time, 'YYYY_MM_DD') ||' (setup_time)';

        EXECUTE 'INSERT INTO cdrs_'|| to_char(NEW.setup_time, 'YYYY_MM_DD') ||' SELECT ($1).*'
        USING NEW;
        RETURN NULL;
END
$$
LANGUAGE plpgsql;


CREATE TRIGGER fk_checkTrigger_cdrs
BEFORE INSERT ON cdrs
FOR EACH ROW
EXECUTE PROCEDURE cdrs_insert_trigger();

现在,当我尝试在同一个分区中插入重复的i_cdrs_connections时,它会显示唯一的密钥冲突,但是当从其他分区中尝试相同的密钥时,从相同的分区行继承而没有错误。

总之,单个分区上的唯一索引工作正常但在表上有多个分区值并不是唯一的。

我知道序列,但对于给定的表使用较少,因为这将从另一个数据库复制,我们必须删除重复插入的机会。

2 个答案:

答案 0 :(得分:1)

这是PostgreSQL的当前行为。唯一索引处理分区,而不是整个表。您有几个选择:

  1. 如果可能的话,对表进行分区,使得键范围在某种程度上是独占的。换句话说,对关键数据进行分区。这是最简单,最轻松的方法。在这里,您要对非关键数据进行分区,这是一个问题。

  2. 如果这不起作用,您可以将分区值添加到联接的另一侧。请注意,此时您需要自定义fkey触发器。

  3. 如果你真的需要,你可以创建一个触发器维护的所有id的物化视图,并在其上创建一个唯一的索引。

答案 1 :(得分:0)

我读了一篇关于此的文章:http://blog.ioguix.net/postgresql/2015/02/05/Partitionning-and-constraints-part-1.html

他发现了一个非常好的解决方案。

所以基本上解决方案是添加锁(咨询锁)并添加一个触发器,如:

 CREATE OR REPLACE FUNCTION public.master_id_pkey()
 RETURNS trigger
 LANGUAGE plpgsql
AS $function$
BEGIN
  PERFORM pg_advisory_xact_lock(NEW.id);

  IF count(1) > 1 FROM master WHERE id = NEW.id THEN
     RAISE EXCEPTION 'duplicate key value violates unique constraint "%" ON "%"', 
      TG_NAME, TG_TABLE_NAME 
      USING DETAIL = format('Key (id)=(%s) already exists.', NEW.id);
  END IF;

  RETURN NULL;
END
$function$;