将SQLite文件合并到一个db文件中,然后“开始/提交”问题

时间:2010-09-11 03:32:32

标签: python merge sqlite

This post引用此page来合并SQLite数据库。

序列如下。假设我要合并a.db和b.db.在命令行中,我执行以下操作。

  • sqlite3 a.db
  • 将'b.db'附加为M;
  • 开始; < -
  • 插入基准select * from toM.benchmark;
  • 提交; < -
  • 分离数据库toM;

它运作良好,但在引用的网站中,提问者询问加速,答案是使用'begin'和'commit'命令。

然后,我想出了以下python代码来完成同样的事情。我用SQLiteDB抽象SQLite函数调用,其中一个方法是runCommand()。即使我删除了self.connector.commit(),我也得到了同样的错误。

# run command
def runCommand(self, command):
    self.cursor.execute(command)
    self.connector.commit() # same error even though I delete this line

db = SQLiteDB('a.db')
cmd = "attach \"%s\" as toMerge" % "b.db"
print cmd
db.runCommand(cmd)
cmd = "begin"
db.runCommand(cmd)
cmd = "insert into benchmark select * from toMerge.benchmark"
db.runCommand(cmd)
cmd = "commit"
db.runCommand(cmd)
cmd = "detach database toMerge"
db.runCommand(cmd)

但是,我收到了以下错误。

OperationalError: cannot commit - no transaction is active

即使出错,结果db也很好地合并了。没有开始/提交,根本就没有错误。

  • 为什么我不能运行begin / commit命令?
  • 是否必须运行begin / commit以安全地合并db文件?帖子说开始/提交的目的是为了加速。那么,就加速而言,使用和不使用begin / commit命令有什么区别?

1 个答案:

答案 0 :(得分:12)

显然,Cursor.execute不支持'commit'命令。它确实支持'begin'命令,但这是多余的,因为sqlite3会为你开始它们:

>>> import sqlite3
>>> conn = sqlite3.connect(':memory:')
>>> cur = conn.cursor()
>>> cur.execute('begin')
<sqlite3.Cursor object at 0x0104B020>
>>> cur.execute('CREATE TABLE test (id INTEGER)')
<sqlite3.Cursor object at 0x0104B020>
>>> cur.execute('INSERT INTO test VALUES (1)')
<sqlite3.Cursor object at 0x0104B020>
>>> cur.execute('commit')

Traceback (most recent call last):
  File "<pyshell#10>", line 1, in <module>
    cur.execute('commit')
OperationalError: cannot commit - no transaction is active
>>> 

只需在commit对象上使用Connection方法。

至于你的第二个问题,在合并文件时调用begin / commit并不是绝对必要的:只要确保没有磁盘错误,对数据库的修改或者以错误的方式查看计算机的人正在发生。所以开始/提交可能是一个好主意。当然,如果原始的数据库没有被修改(我老实说没有看过),那么就没有必要。如果出现错误,您可以废弃部分输出并重新开始。

它还提供了加速,因为每次更改都不必在发生时写入磁盘。它们可以存储在内存中并批量写入。但正如所提到的sqlite3为您处理这个问题。

另外,值得一提的是

cmd = "attach \"%s\" as toMerge" % "b.db"

是错误的,因为它被剥夺了。如果你想正确地做错事,那就是

cmd = 'attach "{0}" as toMerge'.format("b.db") #why not just one string though?

这与新版本的python向前兼容,这将使移植代码更容易。

如果你想做正确的事情,那就是

cmd = "attach ? as toMerge"
cursor.execute(cmd, ('b.db', ))

这样可以避免sql注入,显然会稍快一些,所以它是双赢的。

您可以按如下方式修改runCommand方法:

def runCommand(self, sql, params=(), commit=True):
    self.cursor.execute(sql, params)
    if commit:
        self.connector.commit()

现在,当您不需要提交时,通过传递commit=False,您无法在每个命令之后提交。这保留了交易的概念。