繁体   English   中英

如何在 Postgres 中使用 upsert

[英]How to use upsert with Postgres

我想将 Postgres 中的这段代码转换为更短的代码,可以做同样的事情。 我读到了 upsert,但我不明白在我的代码上实现它的好方法。 我写的东西很好,但我想找到一种更优雅的方式来写它。 希望这里有人可以帮助我:这是查询:

CREATE OR REPLACE FUNCTION insert_table(
    in_guid    character varying,
    in_x_value character varying,
    in_y_value character varying
)
RETURNS TABLE(response boolean) LANGUAGE 'plpgsql'

DECLARE _id integer;

BEGIN
    -- guid exists and it's been 10 minutes from created_date:
    IF ((SELECT COUNT (*) FROM public.tbl_client_location WHERE guid = in_guid AND created_date < NOW() - INTERVAL '10 MINUTE') > 0) THEN
        RETURN QUERY (SELECT FALSE);
    
    -- guid exists but 10 minutes hasen't passed yet:
    ELSEIF ((SELECT COUNT (*) FROM public.tbl_client_location WHERE guid = in_guid) > 0) THEN
    
        UPDATE
            public.tbl_client_location
        SET
            x_value = in_x_value,
            y_value = in_y_value,
            updated_date = now()
        WHERE
            guid = in_guid;

        RETURN QUERY (SELECT TRUE);
    
    -- guid not exist:
    ELSE
       
        INSERT INTO public.tbl_client_location
            ( guid   , x_value   , y_value    )
        VALUES
            ( in_guid, in_x_value, in_y_value )
        RETURNING id INTO _id;  

        RETURN QUERY (SELECT TRUE);

    END IF;
END

这确实可以简单得多

CREATE OR REPLACE FUNCTION insert_table(in_guid text
                                      , in_x_value text
                                      , in_y_value text
                                      , OUT response bool)  -- ④
  -- RETURNS record  -- optional noise  -- ④
  LANGUAGE plpgsql AS  -- ①
$func$  -- ②
-- DECLARE
   -- _id integer;  -- what for?
BEGIN
   INSERT INTO tbl AS t
          (   guid,    x_value,    y_value)
   VALUES (in_guid, in_x_value, in_y_value)
   ON CONFLICT (guid) DO UPDATE  -- guid exists
   SET    (         x_value,          y_value, updated_date)
        = (EXCLUDED.x_value, EXCLUDED.y_value, now())  -- ⑤
   WHERE  t.created_date >= now() - interval '10 minutes'  -- ③ have not passed yet
   -- RETURNING id INTO _id  -- what for?
   ;
   response := FOUND;  -- ⑥
END
$func$;

假设guid定义为UNIQUEPRIMARY KEY ,并且created_date定义为NOT NULL DEFAULT now()

① 语言名称是一个标识符——最好不带引号。

② 缺少 function 主体周围的引号(无效命令)。 看:

③ 仅在 10 分钟未过时才UPDATE 请记住,时间戳是从相应事务开始的时间戳。 因此,请保持交易简短明了。 看:

④ 带有OUT参数且没有RETURNS子句的 function 自动返回单行( record )。 您的原始文件被声明为 set-returning function (0-n 返回行),这没有意义。 看:

⑤ 通常最好使用特殊的EXCLUDED行而不是再次拼出值。 看:

⑤ 也使用短语法来更新多列。 看:

⑥ 要查看是否写入了行,请使用特殊变量FOUND 细微的区别:与你原来的不同,你在事实之后得到truefalse ,说一行实际上已经被写入(或没有)。 在您的原始文件中,触发器或规则可能仍会跳过INSERTUPDATE (不引发异常),并且 function 结果在这种情况下会产生误导。 看:

进一步阅读:


您可能只运行单个 SQL 语句,提供您的值一次

INSERT INTO tbl AS t(guid, x_value,y_value)
VALUES ($in_guid, $in_x_value, $in_y_value)  -- your values here, once
ON CONFLICT (guid) DO UPDATE
SET    (x_value,y_value, updated_date)
     = (EXCLUDED.x_value, EXCLUDED.y_value, now())
WHERE  t.created_date >= now() - interval '10 minutes';

我终于解决了。 我制作了另一个 function 将被调用并检查它是否已经存在以及时间,然后我可以毫无问题地进行 upsert。 这就是我最后所做的:

CREATE OR REPLACE FUNCTION fnc_check_table(
    in_guid character varying)
    RETURNS TABLE(response boolean) 
    LANGUAGE 'plpgsql'

    COST 100
    VOLATILE 
    ROWS 1000
AS $BODY$
BEGIN
IF EXISTS (SELECT FROM tbl WHERE guid = in_guid AND created_date < NOW() - INTERVAL '10 MINUTE' ) THEN
RETURN QUERY (SELECT FALSE);
ELSEIF EXISTS (SELECT FROM tbl WHERE guid = in_guid AND created_date > NOW() - INTERVAL '10 MINUTE') THEN
RETURN QUERY (SELECT TRUE);
ELSE
RETURN QUERY (SELECT TRUE);
END IF;

END
$BODY$;



CREATE OR REPLACE FUNCTION fnc_insert_table(
    in_guid character varying,
    in_x_value character varying,
    in_y_value character varying)
    RETURNS TABLE(response boolean) 
    LANGUAGE 'plpgsql'

    COST 100
    VOLATILE 
    ROWS 1000
AS $BODY$
BEGIN
IF (fnc_check_table(in_guid)) THEN
    INSERT INTO tbl (guid, x_value, y_value)
    VALUES (in_guid,in_x_value,in_y_value)
    ON CONFLICT (guid)
    DO UPDATE SET x_value=in_x_value, y_value=in_y_value, updated_date=now();
    RETURN QUERY (SELECT TRUE);
ELSE
    RETURN QUERY (SELECT FALSE);
END IF;
END
$BODY$;

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM