【问题标题】:Functions with variable number of input parameters输入参数数量可变的函数
【发布时间】:2013-05-28 00:27:53
【问题描述】:

我正在 PostgreSQL 数据库中创建一个存储过程(函数),它根据输入更新表。为了创建可变数量的参数函数,我创建了一个名为 mode 的额外输入参数,用于控制在更新查询中使用哪些参数。

CREATE OR REPLACE FUNCTION update_site(
    mode integer,
    name character varying,
    city character varying,
    telephone integer,
)
RETURNS integer AS
$$
BEGIN
IF mode = 0 THEN
BEGIN
    UPDATE "Sites" SET 
    ("City","Telephone") = (city,telephone)
    WHERE "SiteName" = name;
    RETURN 1;
    EXCEPTION WHEN others THEN
    RAISE NOTICE 'Error on site update: %, %',SQLERRM,SQLSTATE;
    RETURN 0;
END;
ELSIF mode = 1 THEN
BEGIN
    UPDATE "Sites" SET "City" = city
    WHERE "SiteName" = name;
    RETURN 1;
    EXCEPTION WHEN others THEN
    RAISE NOTICE 'Error on site update: %, %',SQLERRM,SQLSTATE;
    RETURN 0;
END;
    ELSIF mode = 2 THEN
BEGIN
    UPDATE "Sites" SET "Telephone" = telephone
    WHERE "SiteName" = name;
    RETURN 1;
    EXCEPTION WHEN others THEN
    RAISE NOTICE 'Error on site update: %, %',SQLERRM,SQLSTATE;
    RETURN 0;
END;
    ELSE
            RAISE NOTICE 'Error on site update: %, %',SQLERRM,SQLSTATE;
    RETURN 0;
    END IF;
END;
$$ LANGUAGE plpgsql;

什么是最好的?要创建一个函数update_site(<all the columns of table>) 和一个单独的函数update_site(id integer, <varchar column to update>),还是使用一个函数中的模式来定义区别?哪个选项更有效?一种独特的功能还是针对每种用途的不同功能?

【问题讨论】:

    标签: sql function postgresql stored-procedures sql-update


    【解决方案1】:

    VARIADIC 甚至polymorphic input types 和动态SQL 等高级功能非常强大。此答案的最后一章提供了一个高级示例:

    但是对于像您这样的简单情况,您可以将默认值用于函数参数。这完全取决于确切的要求。
    如果所讨论的列都定义为NOT NULL,这可能会更简单、更快:

    CREATE OR REPLACE FUNCTION update_site(_name      text    -- always required
                                         , _city      text    DEFAULT NULL
                                         , _telephone integer DEFAULT NULL)
      RETURNS integer AS
    $func$
    BEGIN
       IF _city IS NULL AND _telephone IS NULL THEN
          RAISE WARNING 'At least one value to update required!';
          RETURN;  -- nothing to update
       END IF;
    
       UPDATE "Sites"
       SET    "City"      = COALESCE(_city, "City")
            , "Telephone" = COALESCE(_telephone, "Telephone")
       WHERE  "SiteName"  = _name;
    END
    $func$  LANGUAGE plpgsql;
    

    了解default values in the manual!

    为了避免参数和列名之间的命名冲突,我习惯于在输入参数前加上_。这是品味和风格的问题。

    • 第一个参数name 没有默认值,因为它始终是必需的。
    • 其他参数可以省略。
    • 至少需要一个,否则会引发 WARNING 并且不会发生其他任何事情。
    • UPDATE 只会更改给定参数的列。
    • 可以轻松扩展N个参数。

    函数调用

    Postgres 9.5 起:

    简单的方法是使用positional notation 作为参数。这只允许省略最右边的参数:

    SELECT update_site('foo', 'New York');  -- no telephone
    

    Named notation 允许省略 any 具有默认值的参数:

    SELECT update_site(name => 'foo', _telephone => 123);  -- no city
    

    两者都可以组合在mixed notation:

    SELECT update_site('foo', _telephone => 123);  -- still no city
    

    Postgres 9.4 或更早版本中,:= 用于调用中的分配:

    SELECT update_site(name := 'foo', _telephone := 123);
    SELECT update_site('foo', _telephone := 123);
    

    为了向后兼容在 Postgres 12 中仍然有效,但要使用现代符号。

    【讨论】:

    • 感谢您的回答。对于我的问题,这似乎是一个非常好的解决方案。除了 PostgreSQL 之外,其他数据库编程语言是否可以使用默认值?
    • @kzz:这些规则仅适用于 PostgreSQL 函数。其他 RDBMS 也嵌入了过程语言。 Oracle 的 PL/SQL 是最接近的,PL/pgSQL 是在此基础上建模的。不确定 PL/SQL 或 MS tSQL 如何处理默认值。默认值的概念无疑比VARIADIC 或多态参数更广泛。
    • 我决定将我接受的答案更改为这个答案,因为它更详细地回答了我的问题。
    • 很好...我设法完全错过了对默认参数的支持。
    【解决方案2】:

    您需要研究以下几点:

    • 使用format 函数及其%I%L 说明符动态构建SQL,然后使用EXECUTE ... USING 执行它;和

    • 使用VARIADIC 参数将可变数量的参数传递给函数,但需要注意的是它们都必须是相同的数据类型。

    【讨论】:

    • 感谢您的回答。我找到了关于 EXECUTE 格式的信息,并且可以完成我要求的可变参数,但是由于每次执行函数时都会重新计算 EXECUTE,我想最终它会比普通函数效率低。我想知道这是否可能,但对于我被问到的问题,这比需要的要多。无论如何,谢谢。不过,我肯定会阅读更多有关它的信息。看起来很有趣。
    • @kzz 是的,这是一个有用的工具,但要小心使用EXECUTE ... USING 或安全格式参数,以避免通过数据库内动态 SQL 意外暴露 SQL 注入。
    • 很好的答案!!!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-06-21
    • 2020-07-14
    • 1970-01-01
    • 2022-06-10
    • 2011-01-09
    • 1970-01-01
    • 2022-12-11
    相关资源
    最近更新 更多