【问题标题】:Inserting DEFAULT value into a column when a parameter is NULL当参数为 NULL 时将 DEFAULT 值插入列
【发布时间】:2016-06-26 16:18:03
【问题描述】:

我想写一个这样的存储过程:

CREATE OR REPLACE FUNCTION my_function(param_1 text, param_2 text DEFAULT NULL::text) RETURNS bigint AS
$$
DECLARE ret bigint;
BEGIN
    INSERT INTO my_table(val_1, val_2) VALUES (param_1, param_2);

    -- do some more stuff

    RETURN ret;
END;
$$
LANGUAGE plpgsql;

但是,我想使用val_2 列的DEFAULT 值而不是NULL - 如果NULL 作为param_2 值提供。

类似这样的:

INSERT INTO my_table(val_1, val_2) VALUES (param_1, COALESCE(param_2, DEFAULT));

显然是错误的,因为INSERT 语句规范明确声明了一个表达式OR DEFAULT 可以使用,DEFAULT 本身在表达式中不可用。

我自己找到了两个解决方案,但我对它们并不满意。

  1. 从信息架构中选择DEFAULT 值并将其用于COALESCE 表达式。

我不是专家,但似乎应该有一种更简单、更优雅的方法。

  1. 使用INSERT,然后使用UPDATE

像这样:

-- ...
INSERT INTO my_table(val_1) VALUES (param_1)
RETURNING id INTO id_var;

IF (param_2) IS NOT NULL THEN
    UPDATE my_table SET val_2 = param_2 WHERE id = id_var;
END IF;
-- ...

然而,这个解决方案有一个问题。生产系统的实际表有一些复杂的触发器,这些触发器在此表上的 UPDATE 语句上运行,因此我通常希望尽可能避免使用更新。

一般来说,我可能会坚持第二种解决方案,但这可能需要在上述触发器中添加一些技巧。但如果有办法避免这种情况 - 我将非常感谢您指出这一点。

【问题讨论】:

  • DEFAULT 不能在表达式中使用,所以它不起作用。
  • 这样你就可以构建动态 SQL 解决方案了。
  • 由于DEFAULT 的工作方式,我认为您需要针对每种情况使用不同的INSERT 语句。因此,要么使用IF param_2 IS NULL THEN ... 在两个变体之间进行选择,要么构建一个动态语句并通过EXECUTE 运行它
  • 使用触发器附加/代替默认值。

标签: postgresql stored-procedures plpgsql postgresql-9.4


【解决方案1】:

评论有点长。

一种方法是声明列NOT NULL。插入NULL 值会产生约束冲突,您可以使用on constraint 在insert 中捕获它。

这似乎是一个正确的方法。如果该列具有默认值,那么您可能不希望它为 NULL。

【讨论】:

    【解决方案2】:

    使用动态 SQL:

    CREATE OR REPLACE FUNCTION my_function(param_1 text, param_2 text DEFAULT NULL::text)
      RETURNS bigint AS
    $$
    DECLARE ret bigint;
    BEGIN
    
     EXECUTE 'INSERT INTO my_table(val_1, val_2) VALUES (' ||
              quote_literal(param_1) || ',' ||
              CASE WHEN param_2 IS NULL THEN 'DEFAULT' ELSE quote_literal(param_2) END ||
              ')';
    
     RETURN ret;
    END;
    $$
    LANGUAGE plpgsql;
    

    SqlFiddleDemo

    【讨论】:

      【解决方案3】:

      由于param_2 只能是null 或not null 之一,因此只有一个选择会返回要插入的行:

      with i as (
          insert into my_table (val_1)
          select param_1
          where param_2 is null
      )
      insert into my_table (val_1, val_2)
      select param_1, param_2
      where param_2 is not null
      

      如果需要返回插入的值:

      with i_null as (
          insert into my_table (val_1)
          select param_1
          where param_2 is null
          returning *
      ), i_notnull as (
          insert into my_table (val_1, val_2)
          select param_1, param_2
          where param_2 is not null
          returning *
      )
      select * from i_null
      union all
      select * from i_notnull
      

      【讨论】:

        【解决方案4】:

        如果插入和问题中一样简单,我会使用两个不同的INSERT 语句,因为生成的代码小且可读且快速:

        IF param_2 IS NULL THEN
          INSERT INTO my_table(val_1) VALUES (param_1);
        ELSE
          INSERT INTO my_table(val_1, val_2) VALUES (param_1, param_2);
        END IF;
        

        对于更复杂的插入,例如使用多个可选参数,我可能会选择上面@lad2025 的动态 SQL 解决方案。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2016-10-15
          • 2020-04-15
          • 1970-01-01
          • 2011-09-22
          • 2017-08-26
          • 2010-09-18
          相关资源
          最近更新 更多