简体   繁体   English

Postgres 使用其他表插入冲突更新

[英]Postgres insert on conflict update using other table

I having syntax error for the following sql which spent an hour but cant find any answer form postgres documentation.我有以下 sql 的语法错误,它花了一个小时但无法从 postgres 文档中找到任何答案。

CREATE TABLE transaction (userid SMALLINT, point INT, timestamp TIMESTAMPTZ);
CREATE TABLE point (userid SMALLINT PRIMARY KEY, balance1 INT, balance2 INT);
CREATE TABLE settings (userid SMALLINT, bonus INT);
INSERT INTO settings VALUES (1, 10); -- sample data

WITH trans AS (
  INSERT INTO transaction (userid, point, timestamp) 
  VALUES ($1, $2, NOW()) 
  RETURNING *
)
INSERT INTO point (userid, balance1) 
SELECT userid, point 
FROM trans -- first time
ON CONFLICT (userid) DO UPDATE 
  SET balance1=point.balance1 + excluded.balance1, 
      balance2=point.balance2 + excluded.balance1 + settings.bonus 
FROM settings -- tried USING, not work
WHERE settings.userid=excluded.userid;

My question is how can I include extra table without using FROM during update?我的问题是如何在更新期间不使用 FROM 的情况下包含额外的表? My target is to keep all into 1 query.我的目标是将所有内容保留在 1 个查询中。

It's not totally clear to me what you are trying to do.我不完全清楚你要做什么。 The way I understand it, is that you are trying to use the value from settings.bonus when updating the row in case on conflict kicks in.我的理解是,您在更新行时尝试使用settings.bonus的值,以防on conflict

This can be done using a co-related sub-query in the UPDATE part:这可以使用UPDATE部分中的相关子查询来完成:

WITH trans (userid, point, timestamp) AS (
  INSERT INTO transaction (userid, point, timestamp) 
  VALUES (1, 1, NOW()) 
  RETURNING *
)
INSERT INTO point (userid, balance1) 
SELECT trans.userid, trans.point 
FROM trans 
ON CONFLICT (userid) DO UPDATE 
  SET balance1 = point.balance1 + excluded.balance1, 
      balance2 = point.balance2 + excluded.balance1 + (select s.bonus 
                                                       from settings s 
                                                       where s.userid = excluded.userid)

Note however, that balance2 will never be incremented this way because on the first insert it will be null subsequent updates will try to add something to null but any expression involving null yields null ( null + 5 is null).但是请注意, balance2永远不会以这种方式递增,因为在第一次插入时它将为null后续更新将尝试向null添加一些内容但任何涉及 null 的表达式都会产生 null ( null + 5为 null)。

So you either need to insert 0 when doing the insert the first time, or use coalesce() when doing the update, eg所以你要么在第一次插入时插入0 ,要么在更新时使用coalesce() ,例如

balance2 = coalesce(point.balance2, 0) + excluded.balance1 + (select ...);

If it's possible that there is no row in settings for that user, then you need to apply coalesce() on the result of the sub-query as well:如果该用户的settings可能没有行,那么您还需要对子查询的结果应用coalesce()

... excluded.balance1 + coalesce( (select ... ), 0)

Also: you used excluded.point in your question which is incorrect.另外:您在问题中使用了excluded.point ,这是不正确的。 excluded refers to the target table point but that does not have a column point - you probably meant: excluded.balance1 (as that is the column into which the inserted trans.point will go) excluded是指目标表point但没有列点 - 您可能是说:excluded.balance1(因为这是插入的trans.point将进入的列)


Unrelated, but: timestamp is a horrible name for a column.无关,但是: timestamp对于列来说是一个可怕的名称。 For one because it's a keyword, but more importantly because it does not document what the column is for.一方面是因为它是一个关键字,但更重要的是因为它没有记录该列的用途。 Does it mean "created at"?它的意思是“创建于”吗? "valid until"? “有效期至”? "start at"? “开始于”? "obsolete from"? “过时了”? Something entirely different?完全不同的东西?

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

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