2012-09-06 54 views
0

我有表称之为的CDR:唯一索引在PostgreSQL的分区

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它显示了独特的键冲突,但是当从同一个分区排继承其他分区尝试相同的密钥没有错误添加。

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

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

+1

见这一个:http://stackoverflow.com/questions/936818/postgres-table-inheritance-enforcing -unique-constraints-across-partitions – dezso

+0

这似乎与您刚刚发布的新问题几乎相同。你可以安慰他们还是删除这个旧的? http://stackoverflow.com/questions/12299352/unique-index-over-partition-tables-in-postgresql –

+0

@CraigRinger两个是不同的你提到的要求随机行为的序列。像一个用户也面临几乎相同的问题http://bizzteams.com/forum/postgresql/25727-weird-sequence-increasing-partitioned-table.html但这里的问题是所有子表的唯一索引 – sharafjaffri

回答

1

这是PostgreSQL的当前行为。唯一的索引解决分区问题,而不是整个表。你有几个选择:

  1. 如果可能的话,分区你的表,以便在某些方面的密钥范围是排他性的。换句话说,关键数据的分区。这是最简单,最简单的方法。在这里,您正在对非关键数据进行分区,这是一个问题。

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

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

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$; 
+1

触发器本身是缓慢的。特别是这种触发器的设计非常糟糕。做'IF count(1)> 1'是一个代价高昂的操作,因为它会解析整个表格。相反,“IF EXISTS”并在插入前添加触发器。 – AlexanderMP