当参数为 NULL 时将 DEFAULT 值插入列

Inserting DEFAULT value into a column when a parameter is NULL

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

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. Select 来自信息模式的 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;
-- ...

但是这个解决方案有一个问题。生产系统的实际 table 有一些复杂的触发器 运行 on UPDATE statements on this table 所以我通常希望尽可能避免使用更新。

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

评论有点长

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

insert 中捕捉到这种冲突

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

使用动态-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

因为 param_2 只能是 nullnot null 之一,只有一个选择将 return 插入一行:

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

如果需要return插入的值:

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

如果插入像问题中一样简单,我将使用两个不同的 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 提供的 dynamic-SQL 解决方案。