【发布时间】:2017-09-25 10:43:21
【问题描述】:
我有一个循环遍历一个看起来与此类似的表(为提高可读性而简化了错误处理):
for rows.Next() {
var (
id int
field2 int
field1 int
)
err = rows.Scan(&id, &field1, &field2)
chk(err)
field1 += someFunc()
field2 += someOtherFunc()
err = db.Exec(`UPDATE table SET field1 = ?, field2 = ? WHERE id = ?`, field1, field2, id)
chk(err)
}
如您所见,我想扫描每一行的字段,以某种方式修改它们并更新数据库。 someFunc 和 someOtherFunc 返回的值对于每一行都是不同的,并且无法仅从其字段的值中确定。
这段代码不起作用,因为数据库被锁定直到rows 关闭(我正在使用mattn's go-sqlite3 driver)。此外,它效率不高,因为每次更新行时数据库都需要执行查询。我知道我可以使用db.Prepare,然后在完成迭代后执行所有查询,但这会消耗不必要的内存量并且不会缓解效率问题。
我做了一些阅读,似乎游标提供了我正在寻找的功能(我不是 SQL 专家)并且我使用的 SQL 风格(SQLite3)支持它们,但database/sql 似乎没有到。
database/sql 中是否有任何自然的方式可以在迭代行时更新行?
【问题讨论】:
-
光标仅供阅读。并且您不得更改您正在使用光标阅读的表格。
-
请原谅我的无知,但这->“数据库被锁定直到
rows被关闭”听起来是错误的......这是真的吗?你能指点我一些特别提到这一点的文件吗?还是您使用的 sql 驱动程序的行为方式以及您使用的是什么驱动程序? -
@mkopriva 我编辑了问题以包含驱动程序。在我关闭
rows之前,数据库确实被锁定。它也可能是特定于驱动程序的行为,database/sql的文档中没有任何内容表明它应该以这种方式运行,但也没有任何内容表明它是不允许的。我从未声称这种行为是普遍的。但是,我查看了驱动程序的文档,似乎也没有关于这种行为的内容。 -
很公平。您的代码是否有机会在事务中运行?我记得 postgres 有类似的问题,但要解决它,我需要做的就是在循环内使用单独的 tx(或连接?我不记得确切)。所以基本上我会有两个连接或其他东西,一个用于选择行,另一个用于更新表格。
-
@mkopriva 是的,这似乎是造成这种情况的驱动程序。我现在发现的最优雅的工作解决方案似乎是在循环之前启动事务,将其用于
Exec调用并在循环完成后提交它。这可能仍然使用比它应该使用的更多的内存,但至少它在代码中看起来更好。我想我会考虑在不久的将来换一个不同的驱动程序。