[英]How to use upsert with Postgres
I want to convert this code in Postgres to something shorter that will do the same.我想将 Postgres 中的这段代码转换为更短的代码,可以做同样的事情。 I read about upsert but I couldn't understand a good way to implement that on my code.我读到了 upsert,但我不明白在我的代码上实现它的好方法。 What I wrote works fine, but I want to find a more elegant way to write it.我写的东西很好,但我想找到一种更优雅的方式来写它。 Hope someone here can help me: This is the query:希望这里有人可以帮助我:这是查询:
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
This can indeed be a lot simpler :这确实可以简单得多:
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$;
Assuming guid
is defined UNIQUE
or PRIMARY KEY
, and created_date
is defined NOT NULL DEFAULT now()
.假设guid
定义为UNIQUE
或PRIMARY KEY
,并且created_date
定义为NOT NULL DEFAULT now()
。
① Language name is an identifier - better without quotes. ① 语言名称是一个标识符——最好不带引号。
② Quotes around function body were missing (invalid command). ② 缺少 function 主体周围的引号(无效命令)。 See:看:
③ UPDATE
only if 10 min have not passed yet. ③ 仅在 10 分钟未过时才UPDATE
。 Keep in mind that timestamps are those from the beginning of the respective transactions.请记住,时间戳是从相应事务开始的时间戳。 So keep transactions short and simple.因此,请保持交易简短明了。 See:看:
④ A function with OUT
parameter(s) and no RETURNS
clause returns a single row ( record
) automatically. ④ 带有OUT
参数且没有RETURNS
子句的 function 自动返回单行( record
)。 Your original was declared as set-returning function (0-n returned rows), which didn't make sense.您的原始文件被声明为 set-returning function (0-n 返回行),这没有意义。 See:看:
⑤ It's generally better to use the special EXCLUDED
row than to spell out values again. ⑤ 通常最好使用特殊的EXCLUDED
行而不是再次拼出值。 See:看:
⑤ Also using short syntax for updating multiple columns. ⑤ 也使用短语法来更新多列。 See:看:
⑥ To see whether a row was written use the special variable FOUND
. ⑥ 要查看是否写入了行,请使用特殊变量FOUND
。 Subtle difference: different from your original, you get true
or false
after the fact , saying that a row has actually been written (or not).细微的区别:与你原来的不同,你在事实之后得到true
或false
,说一行实际上已经被写入(或没有)。 In your original, the INSERT
or UPDATE
might still be skipped (without raising an exception) by a trigger or rule, and the function result would be misleading in this case.在您的原始文件中,触发器或规则可能仍会跳过INSERT
或UPDATE
(不引发异常),并且 function 结果在这种情况下会产生误导。 See:看:
Further reading:进一步阅读:
You might just run the single SQL statement instead, providing your values once :您可能只运行单个 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';
I finally solved it.我终于解决了。 I made another function that'll be called and checked if it's already exists and the time and then I can do upsert without any problems.我制作了另一个 function 将被调用并检查它是否已经存在以及时间,然后我可以毫无问题地进行 upsert。 That's what I did at the end:这就是我最后所做的:
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.