pg-promise项目教程:实现SELECT后INSERT的原子操作

pg-promise项目教程:实现SELECT后INSERT的原子操作

pg-promise PostgreSQL interface for Node.js pg-promise 项目地址: https://gitcode.com/gh_mirrors/pg/pg-promise

引言

在数据库操作中,"先查询后插入"是一个非常常见的业务场景。本文将详细介绍如何使用pg-promise库在PostgreSQL中实现这一模式的原子操作,包括传统方法和高性能的单查询方案。

业务场景分析

考虑这样一个典型需求:

  1. 首先尝试查询特定条件的记录
  2. 如果记录存在,返回其ID
  3. 如果不存在,则插入新记录并返回新ID

这种模式在用户系统、标签系统等场景中非常常见,比如根据用户名获取用户ID,如果用户不存在则自动创建。

基础表结构

我们先定义一个简单的用户表作为示例:

CREATE TABLE Users(
    id SERIAL PRIMARY KEY,
    name TEXT UNIQUE -- 用户名唯一
);

传统实现方案

基本实现

使用pg-promise的task方法可以确保整个操作在一个事务中完成:

function getInsertUserId(name) {
    return db.task('getInsertUserId', t => {
        return t.oneOrNone('SELECT id FROM Users WHERE name = $1', name, u => u && u.id)
            .then(userId => {
                return userId || t.one('INSERT INTO Users(name) VALUES($1) RETURNING id', name, u => u.id);
            });
    });
}

使用说明

调用这个函数非常简单:

getInsertUserId('张三')
    .then(userId => {
        console.log('获取到的用户ID:', userId);
    })
    .catch(error => {
        console.error('操作失败:', error);
    });

ES7 async/await版本

对于支持ES7的环境,可以使用更简洁的async/await语法:

async function getInsertUserId(name) {
    return db.task('getInsertUserId', async t => {
        const userId = await t.oneOrNone('SELECT id FROM Users WHERE name = $1', name, u => u && u.id);
        return userId || await t.one('INSERT INTO Users(name) VALUES($1) RETURNING id', name, u => u.id);
    });
}

高性能单查询方案

虽然上述方案可以工作,但它有两个潜在问题:

  1. 需要执行两次数据库查询,影响性能
  2. 在高并发场景下可能出现竞态条件

单查询的优势

将整个操作封装为单个SQL查询可以:

  • 显著提高性能(只需一次数据库往返)
  • 确保数据完整性(避免并发插入冲突)
  • 减少网络开销

实现思路

PostgreSQL 9.5+可以使用ON CONFLICT语法实现:

INSERT INTO Users(name) 
VALUES($1) 
ON CONFLICT (name) 
DO UPDATE SET name = EXCLUDED.name 
RETURNING id;

对于旧版本,可以使用CTE(Common Table Expressions)实现类似功能:

WITH sel AS (
    SELECT id FROM Users WHERE name = $1
), ins AS (
    INSERT INTO Users(name)
    SELECT $1
    WHERE NOT EXISTS (SELECT 1 FROM sel)
    RETURNING id
)
SELECT id FROM sel
UNION ALL
SELECT id FROM ins;

最佳实践建议

  1. 并发控制:在可能并发的场景下,优先使用单查询方案
  2. 错误处理:始终处理可能出现的唯一约束冲突错误
  3. 事务使用:即使使用单查询,复杂业务逻辑仍建议放在事务中
  4. 索引优化:确保查询字段(如name)有适当索引

总结

本文介绍了在pg-promise中实现"先查询后插入"模式的多种方法。对于简单场景,传统的事务方案足够使用;而对于高性能和高并发需求,建议采用单查询方案。具体选择哪种方案,开发者需要根据实际业务需求、PostgreSQL版本和性能要求来决定。

记住,数据库操作的核心原则是:在保证数据完整性的前提下,尽可能提高性能。pg-promise提供的灵活API让我们能够根据具体场景选择最合适的实现方式。

pg-promise PostgreSQL interface for Node.js pg-promise 项目地址: https://gitcode.com/gh_mirrors/pg/pg-promise

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

尚绮令Imogen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值