Postgre SQL数据库实现有记录则更新无记录就新增的方法

发布时间:2021-07-05 16:58:09 作者:chen
来源:亿速云 阅读:420
# PostgreSQL数据库实现有记录则更新无记录就新增的方法

## 引言

在数据库操作中,"有记录则更新,无记录则新增"(通常称为"upsert"操作)是一个常见需求。PostgreSQL提供了多种方式实现这一功能,本文将详细介绍三种主流方法:`ON CONFLICT`子句、可写CTE以及函数/存储过程实现。

## 方法一:ON CONFLICT子句(推荐)

### 基本语法

```sql
INSERT INTO table_name (column1, column2, ...)
VALUES (value1, value2, ...)
ON CONFLICT (conflict_target) 
DO UPDATE SET column1 = excluded.column1, ...;

实际案例

假设有用户表users,主键为user_id

-- 创建测试表
CREATE TABLE users (
    user_id INT PRIMARY KEY,
    username VARCHAR(50) NOT NULL,
    last_login TIMESTAMP,
    login_count INT DEFAULT 0
);

-- Upsert操作
INSERT INTO users (user_id, username, last_login, login_count)
VALUES (1, 'john_doe', NOW(), 1)
ON CONFLICT (user_id) 
DO UPDATE SET 
    last_login = EXCLUDED.last_login,
    login_count = users.login_count + 1;

注意事项

  1. conflict_target必须是唯一约束或主键
  2. EXCLUDED伪表包含被拒绝的插入行
  3. 可以添加WHERE条件实现条件更新

方法二:可写CTE实现

实现原理

通过WITH子句先尝试更新,再根据影响行数决定是否插入

WITH update_cte AS (
    UPDATE users 
    SET last_login = NOW(),
        login_count = login_count + 1
    WHERE user_id = 1
    RETURNING user_id
)
INSERT INTO users (user_id, username, last_login, login_count)
SELECT 1, 'john_doe', NOW(), 1
WHERE NOT EXISTS (SELECT 1 FROM update_cte);

适用场景

  1. 需要更复杂的更新逻辑时
  2. PostgreSQL 9.4及以下版本(不支持ON CONFLICT)

方法三:函数/存储过程实现

PL/pgSQL函数示例

CREATE OR REPLACE FUNCTION upsert_user(
    p_user_id INT,
    p_username VARCHAR,
    p_last_login TIMESTAMP
) RETURNS VOID AS $$
BEGIN
    -- 先尝试更新
    UPDATE users 
    SET username = p_username,
        last_login = p_last_login,
        login_count = login_count + 1
    WHERE user_id = p_user_id;
    
    -- 如果未更新则插入
    IF NOT FOUND THEN
        INSERT INTO users (user_id, username, last_login, login_count)
        VALUES (p_user_id, p_username, p_last_login, 1);
    END IF;
END;
$$ LANGUAGE plpgsql;

调用方式

SELECT upsert_user(1, 'john_doe', NOW());

性能对比

方法 优点 缺点 适用版本
ON CONFLICT 语法简洁,单次原子操作 需要唯一约束 PostgreSQL 9.5+
可写CTE 灵活,支持复杂逻辑 需要两次操作 PostgreSQL 9.1+
函数/存储过程 可封装复杂业务逻辑 需要创建函数 所有版本

高级应用场景

批量Upsert操作

INSERT INTO users (user_id, username, last_login)
VALUES 
    (1, 'john_doe', NOW()),
    (2, 'jane_smith', NOW())
ON CONFLICT (user_id) 
DO UPDATE SET 
    username = EXCLUDED.username,
    last_login = EXCLUDED.last_login;

条件更新

INSERT INTO users (user_id, username, last_login)
VALUES (1, 'john_doe', NOW())
ON CONFLICT (user_id) 
DO UPDATE SET 
    last_login = EXCLUDED.last_login
WHERE users.last_login < EXCLUDED.last_login;

常见问题解答

Q:没有唯一约束时如何实现upsert?

A:可以先创建唯一索引:

CREATE UNIQUE INDEX idx_user_id ON users(user_id);

Q:如何获取upsert后的数据?

A:添加RETURNING子句:

INSERT INTO users (...) VALUES (...)
ON CONFLICT (...) DO UPDATE SET ...
RETURNING *;

Q:ON CONFLICT支持多列唯一约束吗?

A:支持,语法示例:

ON CONFLICT (col1, col2) DO UPDATE ...

结论

PostgreSQL提供了多种灵活的upsert实现方式,推荐优先使用ON CONFLICT语法,它在现代PostgreSQL版本中最简洁高效。对于复杂场景,可写CTE和存储过程提供了更多控制权。根据具体业务需求选择最适合的方案,可以显著提高数据操作的效率和可靠性。

注意:实际应用时应考虑事务隔离级别和并发控制,确保数据一致性。 “`

这篇文章包含了约1200字,采用Markdown格式编写,涵盖了PostgreSQL实现upsert操作的三种主要方法,包含代码示例、性能对比和常见问题解答,适合作为技术文档参考。

推荐阅读:
  1. select into outfile写文件时路径包含中文解决办法
  2. hadoop各个组件功能

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

数据库 postgre sql

上一篇:Python主线程与子线程的区别是什么

下一篇:Python 中有哪些测试框架

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》