带有特定子句的 upsert postgresql

upsert with spesific clause postgresql

我需要一些关于在 postgres 中更新插入的帮助

例如,我有一个 table 结构,如:

CREATE TABLE users_recency(
  id INTEGER PRIMARY KEY,
  created_date TIMESTAMP WITHOUT TIME ZONE,
  first_transaction TIMESTAMP WITHOUT TIME ZONE
 )
 
 CREATE TABLE users_transaction(
   id INTEGER,
   users_id INTEGER,
   transaction_date TIMESTAMP WITHOUT TIME ZONE
 )

如果 users_id 不存在于用户数据库中,我想做的是插入新用户,然后如果 users_transaction.transaction_date < users_recency.first_transaction.[= 更新 first_transaction 13=]

我尝试使用

INSERT INTO users_recency(id, first_transaction)
SELECT users_id, MIN(transaction_date) FROM users_transaction GROUP BY users_id
ON CONFLICT (id) 
DO 
   UPDATE SET first_transaction = EXCLUDED.first_transaction;

此查询更新了所有数据。有什么办法可以实现吗?

这里有一个数据库 fiddle 可以试试

https://www.db-fiddle.com/f/nKk3uaucfRB46kXcGokPKB/1

您的查询缺少 WHERE 条件,该条件将限制 first_transaction 的修改,以防它小于 transaction_date 值。

更新查询:

INSERT INTO users_recency(id, first_transaction)
SELECT users_id, MIN(transaction_date) FROM users_transaction GROUP BY users_id
ON CONFLICT (id)
DO
UPDATE SET first_transaction = EXCLUDED.first_transaction
WHERE EXCLUDED.first_transaction < users_recency.first_transaction;

db fiddle link 为了更好地理解:

https://dbfiddle.uk/?rdbms=postgres_14&fiddle=a8d6b6601424006219dbec5db47817c9

一般Insert语法:

[ WITH [ RECURSIVE ] with_query [, ...] ]
INSERT INTO table_name [ AS alias ] [ ( column_name [, ...] ) ]
    [ OVERRIDING { SYSTEM | USER } VALUE ]
    { DEFAULT VALUES | VALUES ( { expression | DEFAULT } [, ...] ) [, ...] | query }
    [ ON CONFLICT [ conflict_target ] conflict_action ]
    [ RETURNING * | output_expression [ [ AS ] output_name ] [, ...] ]

where conflict_target can be one of:

    ( { index_column_name | ( index_expression ) } [ COLLATE collation ] [ opclass ] [, ...] ) [ WHERE index_predicate ]
    ON CONSTRAINT constraint_name

and conflict_action is one of:

    DO NOTHING
    DO UPDATE SET { column_name = { expression | DEFAULT } |
                    ( column_name [, ...] ) = [ ROW ] ( { expression | DEFAULT } [, ...] ) |
                    ( column_name [, ...] ) = ( sub-SELECT )
                  } [, ...]
              [ WHERE condition ]

有关 Insert 的更多信息,请查看以下官方文档 link:

https://www.postgresql.org/docs/current/sql-insert.html

通过触发器。 demo

CREATE OR REPLACE FUNCTION users_txn_trg_func ()
    RETURNS TRIGGER
    AS $$
DECLARE
    min_tx_date date;
BEGIN
    IF EXISTS (
        SELECT
        FROM
            users_transaction u
        WHERE
            u.users_id = NEW.users_id) THEN
    SELECT
        min(transaction_date) INTO min_tx_date
    FROM
        users_transaction
    WHERE
        users_id = NEW.users_id;
    IF NEW.transaction_date < min_tx_date THEN
        min_tx_date := NEW.transaction_date;
    END IF;
    RAISE info 'new user_id: %, min_tx_date: %', NEW.users_id, min_tx_date;
ELSE
    min_tx_date := NEW.transaction_date;
    RAISE NOTICE 'min_tx_date: %', min_tx_date;
END IF;
INSERT INTO users_recency (id, first_transaction)
    VALUES (NEW.users_id, min_tx_date)
ON CONFLICT (id)
    DO UPDATE SET
        first_transaction = EXCLUDED.first_transaction;
    RETURN new;
END;
$$
LANGUAGE plpgsql;

CREATE TRIGGER trigger_name
    BEFORE INSERT ON users_transaction FOR EACH ROW
    EXECUTE PROCEDURE users_txn_trg_func ();