2012-07-24 207 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