首页 > 后端开发 > Golang > 正文

高效SQL选择与更新:PostgreSQL中的正确姿势

碧海醫心
发布: 2025-10-23 08:30:31
原创
383人浏览过

高效sql选择与更新:postgresql中的正确姿势

本文旨在指导开发者如何在PostgreSQL数据库中,高效且安全地进行数据选择与更新操作。通过结合`SELECT ... FOR UPDATE`语句和事务控制,确保数据一致性。更进一步,探讨使用`UPDATE ... FROM`等集合操作,以优化性能,避免循环更新带来的潜在问题。

在PostgreSQL中,同时进行选择(SELECT)和更新(UPDATE)操作时,需要特别注意数据一致性和并发控制。直接在SELECT循环中执行UPDATE语句可能会导致锁竞争和性能问题。以下介绍几种更安全和高效的方法。

1. 使用 SELECT ... FOR UPDATE 锁定行

最基本的方法是在SELECT语句中使用FOR UPDATE子句。这会锁定选定的行,防止其他事务在当前事务完成之前修改这些行。必须在事务中执行此操作,并在更新完所有行后提交事务。

BEGIN; -- 开启事务

SELECT id, condition, task FROM todos FOR UPDATE;

-- 循环处理结果集,并执行更新操作
-- ...

COMMIT; -- 提交事务
登录后复制

示例(Go语言):

import (
    "database/sql"
    "fmt"
    "log"

    _ "github.com/lib/pq" // PostgreSQL driver
)

func UpdateTasks(db *sql.DB) error {
    tx, err := db.Begin()
    if err != nil {
        return err
    }
    defer func() {
        if p := recover(); p != nil {
            tx.Rollback()
            panic(p) // re-throw panic after Rollback
        } else if err != nil {
            tx.Rollback()
            return
        } else {
            err = tx.Commit()
            if err != nil {
                log.Println("Commit error:", err)
            }
        }
    }()

    rows, err := tx.Query("SELECT id, condition, task FROM todos FOR UPDATE")
    if err != nil {
        return err
    }
    defer rows.Close()

    for rows.Next() {
        var id int
        var condition int
        var task string
        if err := rows.Scan(&id, &condition, &task); err != nil {
            return err
        }

        if condition == 0 {
            newTask := fmt.Sprintf("Updated task for id %d", id)
            _, err = tx.Exec("UPDATE todos SET task = $1 WHERE id = $2", newTask, id)
            if err != nil {
                return err
            }
            log.Printf("Updated task for id %d to '%s'\n", id, newTask)
        }
    }

    if err := rows.Err(); err != nil {
        return err
    }

    return nil
}

func main() {
    dbinfo := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=disable",
        "localhost", 5432, "postgres", "password", "mydatabase")

    db, err := sql.Open("postgres", dbinfo)
    if err != nil {
        log.Fatal(err)
    }
    defer db.Close()

    err = UpdateTasks(db)
    if err != nil {
        log.Fatal(err)
    }
}
登录后复制

注意事项:

  • 必须在事务中使用FOR UPDATE。
  • 在处理完所有行后,必须提交事务。
  • FOR UPDATE 仅阻止其他使用 FOR UPDATE 或 FOR SHARE 的 SELECT 语句访问被锁定的行。正常的 SELECT 语句仍然可以读取这些行。

2. 使用 UPDATE ... FROM 进行集合操作

更高效的方法是尝试将整个操作重写为一个 UPDATE ... FROM 语句。 这种方法允许您在单个查询中更新所有行,避免了循环和锁竞争。

AI新媒体文章
AI新媒体文章

专为新媒体人打造的AI写作工具,提供“选题创作”、“文章重写”、“爆款标题”等功能

AI新媒体文章75
查看详情 AI新媒体文章
UPDATE todos
SET task = 'new task'
FROM (SELECT id FROM todos WHERE condition = 0) AS subquery
WHERE todos.id = subquery.id;
登录后复制

解释:

  • UPDATE todos: 指定要更新的表。
  • SET task = 'new task': 设置 task 列的新值。
  • FROM (SELECT id FROM todos WHERE condition = 0) AS subquery: 创建一个子查询,选择需要更新的行的 id。
  • WHERE todos.id = subquery.id: 将 todos 表与子查询的结果连接起来,以便只更新符合条件的行。

示例 (更新task为基于id的特定值):

UPDATE todos
SET task = 'Task for id ' || subquery.id::text
FROM (SELECT id FROM todos WHERE condition = 0) AS subquery
WHERE todos.id = subquery.id;
登录后复制

3. 总结

| 方法 | 优点 | 缺点 Update todos表中的数据。

4. 总结

选择哪种方法取决于具体的需求和数据量。

  • SELECT ... FOR UPDATE 适用于需要逐行处理和更新少量数据的情况。
  • UPDATE ... FROM 适用于需要根据条件批量更新大量数据的情况,通常性能更好。

在实际应用中,应该根据具体情况选择最合适的方法,并始终注意数据一致性和并发控制。 优先考虑使用集合操作,避免循环更新,以提高性能并减少锁竞争。

以上就是高效SQL选择与更新:PostgreSQL中的正确姿势的详细内容,更多请关注php中文网其它相关文章!

最佳 Windows 性能的顶级免费优化软件
最佳 Windows 性能的顶级免费优化软件

每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习
PHP中文网抖音号
发现有趣的

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号