【问题标题】:Optimize plpgsql function优化plpgsql函数
【发布时间】:2012-07-24 01:56:57
【问题描述】:

PostgreSQL 版本是 9.0。

我必须优化一个 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;

也许最好决定插入触发器,在每个文档插入时在doc_tip_cifra 上插入 4 个空行,以避免对所有文档进行这种愚蠢的昂贵循环并为每个文档测试 4 次? 你怎么看?

【问题讨论】:

    标签: sql performance postgresql insert plpgsql


    【解决方案1】:

    事实证明,您实际上并不需要计数。您的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


    是的,用触发器解决这个问题听起来是个好主意。我会做的。

    【讨论】:

    • 非常感谢!我只是在学习 postgres,你真的用这个让我大开眼界。酷..
    猜你喜欢
    • 2021-02-13
    • 2013-02-03
    • 2012-03-26
    • 2016-03-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多