2012-09-06 90 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