插入COALESCE(NULL,默认值)

时间:2016-08-26 11:47:59

标签: sql postgresql plpgsql

我有使用UUID的表。我希望能够在有或没有UUID的情况下插入新行,因为有时客户端会生成UUID,否则它将不会生成UUID。

每个表都有它的核心:

<p:dashboard id="sortDashboard" model="#{customAttributesSettings.sortModel}" disabled="#{!customAttributesSettings.editable}">
    <p:ajax event="reorder" listener="#{customAttributesSettings.changedSort}" update="@form:customTabSet:sortCurrent" immediate="true"/>
    <c:forEach var="sortColumn" items="#{customAttributesSettings.availableSortColumns}">
        <p:panel id="#{sortColumn.id}" header="#{sortColumn.name}">
            <!--some components-->
        </p:panel>
    </c:forEach>
</p:dashboard>

我正在尝试使用函数插入行。我希望能够传递一个NULL id并获得一个默认值(生成的UUID)。我有这样的事情:

CREATE TABLE IF NOT EXISTS person (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid()
);

我试过这个:

CREATE OR REPLACE FUNCTION create_person(
    id UUID
) RETURNS BOOLEAN LANGUAGE plpgsql SECURITY DEFINER AS $$
BEGIN
    INSERT INTO person( id )
    VALUES (
        COALESCE(id,default)
    );
    RETURN FOUND;
END;
$$;

和此:

INSERT INTO person ( id ) VALUES (
    COALESCE(id, default),
);

这样可行,但它会重复gen_random_uuid()代码:

INSERT INTO person ( id ) VALUES (
  CASE WHEN id IS NULL THEN default ELSE id END
);

同样这也有效,但也存在同样的问题:

INSERT INTO person ( id ) VALUES (
  COALESCE(id, gen_random_uuid()),
);

有没有办法在不重复INSERT INTO person ( id ) VALUES ( CASE WHEN id IS NULL THEN gen_random_uuid() ELSE id END ); 代码的情况下执行此操作?

使用gen_random_uuid()更好吗?

3 个答案:

答案 0 :(得分:6)

无法在列上重复使用已定义的默认值。默认值仅用于定义INSERT未指定值时会发生什么。根据此定义,null值仍然是“指定的”,因此无法使用默认值。

您的评论表明有人可能不使用该功能表示触发器比您的简单功能更符合您的要求。

https://www.postgresql.org/docs/current/static/plpgsql-trigger.html

CREATE OR REPLACE FUNCTION default_id() RETURNS TRIGGER AS $default_id$
    BEGIN       
        IF (NEW.id IS NULL) THEN
            NEW.id := gen_random_uuid();
        END IF;
        RETURN NEW;
    END;
$default_id$ LANGUAGE plpgsql;

CREATE TRIGGER default_id_trigger
BEFORE INSERT OR UPDATE ON person
    FOR EACH ROW EXECUTE PROCEDURE default_id();

如果你想用函数做这个,那么最简单的方法就是在插入之前分配值:

CREATE OR REPLACE FUNCTION create_person(
    id UUID
) RETURNS BOOLEAN LANGUAGE plpgsql SECURITY DEFINER AS $$
BEGIN
    IF id IS NULL THEN
        id := gen_random_uuid();
    END IF;
    -- OR
    -- id := coalesce(id, gen_random_uuid());
    INSERT INTO person( id )
    VALUES (id);
    RETURN FOUND;
END;
$$;

答案 1 :(得分:6)

核心问题是附加到DEFAULT的{​​{1}}表达式中关键字VALUES的特殊性。 Per documentation

  

在显示在INSERT顶层的VALUES列表中,   表达式可以由INSERT替换,以指示目标   应插入列的默认值。 DEFAULT时无法使用   DEFAULT出现在其他情境中。

大胆强调我的。具体来说,VALUES不能成为函数的参数:

DEFAULT

可能的解决方案

有多种方法,具体取决于完全要求。

这个函数不需要知道COALESCE(function_parameter, DEFAULT) -- not possible的实际默认值 - 这似乎是你所追求的:

person.id

避免对参数和涉及的表列使用相同的名称。由于函数体中的每个SQL命令中都可以看到函数参数,因此可能会导致非常混乱的命名冲突(即使现代Postgres中的CREATE OR REPLACE FUNCTION create_person(_id UUID) RETURNS boolean LANGUAGE plpgsql SECURITY DEFINER AS $func$ BEGIN IF _id IS NULL THEN -- no UUID provided INSERT INTO myschema.person(id) -- see below about schema name VALUES (DEFAULT); -- use the key word DEFAULT ELSE -- UUID provided INSERT INTO myschema.person(id) VALUES (_id); END IF; RETURN FOUND; -- (return value pointless so far) END $func$; 的目标列不受此限制)。我使用INSERT作为参数名称。

_id中未提及的 其他列 的默认值已自动填入 。我使用关键字INSERT,因为我们需要列出DEFAULT的至少一个目标列。

此演示中的INSERT返回值毫无意义,因为总是 boolean(除非您有可能跳过该行的触发器)。

相关答案以及可能的替代方案和大量解释:

除此之外:您应该在true函数 - <中对 所有 函数和表名进行架构限定/ strong>(如果你不确定,可能会更好)明确设置SECURITY DEFINER以防御可能的攻击。更多:

答案 2 :(得分:0)

重载gen_random_uuid功能:

create or replace function gen_random_uuid()
returns uuid as $$
    select 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11'::uuid;
$$ language sql;


create or replace function gen_random_uuid(_id uuid)
returns uuid as $$
    select coalesce(_id, gen_random_uuid());
$$ language sql;

像往常一样创建表格:

create table if not exists person (
    id uuid default gen_random_uuid()
);

调用gen_random_uuid函数中的create_person函数:

create or replace function create_person(_id uuid)
returns boolean as $$
    insert into person (id) values (gen_random_uuid(_id))
    returning true;
$$ language sql;

然后所有插入变体都有效:

insert into person (id) values (default);
select create_person(null);
select create_person('b1eebc99-9c0b-4ef8-bb6d-6bb9bd380a22'::uuid);