2012-07-24 79 views
3

PostgreSQL版本爲9.0。優化plpgsql函數

我必須優化一個plpgsql函數。這個想法只是運行所有文檔,並測試表webdte.doc_tip_cifra中的相關行902,903,905,907是否已經存在。如果它們不存在,則插入空行以滿足之後的驗證。即使我只使用4個條件中的一個,並使用其中一半的行數來運行,它現在的速度也很慢。任何人都有提高性能的想法?

CREATE OR REPLACE FUNCTION webdte.addtagobligatoriosventa(idlibro bigint) 
    RETURNS character AS 
$BODY$ 
DECLARE     
    id_documento bigint; 
    validador integer; 
    validador1 integer; 
    validador2 integer; 
    validador3 integer; 
    validador4 integer; 

    tipo_cifra integer; 
    --counts integer[]; 
BEGIN 
    SELECT INTO validador1, validador2, validador3, validador4 
      max(CASE id_tipo_cifra WHEN 901 THEN 1 ELSE 0 END) 
      ,max(CASE id_tipo_cifra WHEN 902 THEN 1 ELSE 0 END) 
      ,max(CASE id_tipo_cifra WHEN 905 THEN 1 ELSE 0 END) 
      ,max(CASE id_tipo_cifra WHEN 907 THEN 1 ELSE 0 END) 
    FROM webdte.doc_tip_cifra 
    WHERE id_doc = id_documento; 

    if (validador1 = 0) then 
     insert into webdte.doc_tip_cifra (id_doc, id_tipo_cifra, tasa_imp, val_imp) 
     values (id_documento, 901, 0, 0); 

    end if; 
     if (validador2 = 0) then 
     insert into webdte.doc_tip_cifra (id_doc, id_tipo_cifra, tasa_imp, val_imp) 
     values (id_documento, 902, 0, 0); 

    end if; 
     if (validador3 = 0) then 
     insert into webdte.doc_tip_cifra (id_doc, id_tipo_cifra, tasa_imp, val_imp) 
     values (id_documento, 905, 0, 0); 

    end if; 
     if (validador4 = 0) then 
     insert into webdte.doc_tip_cifra (id_doc, id_tipo_cifra, tasa_imp, val_imp) 
     values (id_documento, 907, 0, 0); 

    end if; 

END; 
$BODY$ 
    LANGUAGE plpgsql VOLATILE; 

也許最好是決定一個INSERT觸發器,即插入每個文檔中插入的doc_tip_cifra 4個空行,以避免對所有文件和檢驗4次,每次文件這個愚蠢昂貴的循環? 你覺得呢?

回答

2

原來,你並不需要計數。你的preceding question已經傳達了這種印象。然而,在我的解決方案中,它並沒有讓你走得很遠,只用sum替換爲max

它的作品,是的,但它是瘋狂的低效率。在找到匹配的行後,您不必遍歷表格的其餘部分。這就是EXISTS semi-joins。我提出這個完全不同的方法:

INSERT INTO webdte.doc_tip_cifra (id_doc, id_tipo_cifra, tasa_imp, val_imp) 
SELECT id_documento, 901, 0, 0 
WHERE NOT EXISTS (
    SELECT 1 
    FROM webdte.doc_tip_cifra 
    WHERE id_doc = id_documento 
    AND id_tipo_cifra = 901 
    ); 

INSERT INTO webdte.doc_tip_cifra (id_doc, id_tipo_cifra, tasa_imp, val_imp) 
SELECT id_documento, 902, 0, 0 
WHERE NOT EXISTS (
    SELECT 1 
    FROM webdte.doc_tip_cifra 
    WHERE id_doc = id_documento 
    AND id_tipo_cifra = 902 
    ); 

INSERT INTO webdte.doc_tip_cifra (id_doc, id_tipo_cifra, tasa_imp, val_imp) 
SELECT id_documento, 905, 0, 0 
WHERE NOT EXISTS (
    SELECT 1 
    FROM webdte.doc_tip_cifra 
    WHERE id_doc = id_documento 
    AND id_tipo_cifra = 905 
    ); 

INSERT INTO webdte.doc_tip_cifra (id_doc, id_tipo_cifra, tasa_imp, val_imp) 
SELECT id_documento, 907, 0, 0 
WHERE NOT EXISTS (
    SELECT 1 
    FROM webdte.doc_tip_cifra 
    WHERE id_doc = id_documento 
    AND id_tipo_cifra = 907 
    ); 

您可以在PLPGSQL或SQL函數把這個包或者你可以運行它作爲普通的SQL。

除了你之前的問題,這很可能會使用合適的索引。優化將是一個multi-column index像:

CREATE INDEX doc_tip_cifra_special_idx 
ON webdte.doc_tip_cifra (id_doc, id_tipo_cifra); 

應該讓你的查詢快如閃電。

此外,這種算法有一個固有的問題併發。檢查一行是否已經存在並插入它的時間窗口應儘可能小。把它全部放在一個查詢中是最佳的。

不過,它不是完美。如果你的數據庫看到很多併發性,你可能會對這個excellent blog post by @depesz感興趣,或者閱讀更多under this related question


是的,用觸發器解決這個問題聽起來像個好主意。我會做。

+0

非常感謝你!我只是在學習postgres,你真的打開了我的眼睛與這一個。涼.. – mugdiman 2012-07-24 04:05:45