2010-09-11 12 views
5

This post hace referencia a este page para fusionar bases de datos SQLite.Combinar archivos SQLite en un archivo db, y la pregunta 'begin/commit'

La secuencia es la siguiente. Digamos que quiero fusionar a.db y b.db. En la línea de comando, hago lo siguiente.

  • sqlite3 a.db
  • adjuntar 'b.db' como Tom;
  • begin; < -
  • inserte en el banco de pruebas seleccione * de toM.benchmark;
  • commit; < -
  • separar la base de datos aM;

Funciona bien, pero en el sitio referido, el interlocutor pregunta acerca de la aceleración, y la respuesta es usar el comando 'comenzar' y 'confirmar'.

Luego, se me ocurrió el siguiente código python para hacer exactamente lo mismo. Resumo las llamadas a la función SQLite con SQLiteDB, y uno de sus métodos es runCommand(). Obtuve el mismo error aunque eliminé el archivo 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) 

Pero me sale el siguiente error.

OperationalError: cannot commit - no transaction is active 

Aunque el error, el resultado db está bien fusionado. Y sin el inicio/compromiso, no hay ningún error en absoluto.

  • ¿Por qué no puedo ejecutar el comando begin/commit?
  • ¿Es absolutamente necesario ejecutar begin/commit para fusionar de forma segura los archivos db? La publicación dice que el propósito de comenzar/comprometer es para acelerar. Entonces, ¿cuál es la diferencia entre usar y no usar el comando begin/commit en términos de aceleración?

Respuesta

11

Aparentemente, Cursor.execute no es compatible con el comando 'confirmar'. Que es compatible con el comando 'comenzar' pero esto es redundante porque sqlite3 ellas comienza para usted anway:

>>> 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 
>>> 

sólo tiene que utilizar el método commit en el objeto Connection.

En cuanto a su segunda pregunta, no es absolutamente necesario llamar a begin/commit al fusionar los archivos: solo asegúrese de que no haya absolutamente ningún error de disco, modificaciones en los db o personas que miran la computadora de forma incorrecta mientras esta pasando. Entonces comenzar/comprometer es probablemente una buena idea. Por supuesto, si los db originales no se están modificando (sinceramente no los he visto), entonces no hay necesidad de eso ni siquiera. Si hay un error, puede eliminar la salida parcial y comenzar de nuevo.

También proporciona una aceleración porque cada cambio no tiene que escribirse en el disco cuando se produce. Se pueden almacenar en la memoria y escribir a granel. Pero como se menciona sqlite3 maneja esto para usted.

Además, vale la pena mencionar que

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

está mal en el sentido de que es depracated. Si usted quiere hacer las cosas mal correctamente, es

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

Esto es compatible con versiones futuras versiones más recientes del pitón que hará más fácil portar código.

si usted quiere hacer la derecha cosa, es

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

Esto evita la inyección de SQL y es, al parecer, un poco más rápido por lo que es ganar-ganar.

podrá modificar su método de runCommand de la siguiente manera:

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

ahora no se puede cometer después de cada comando pasando commit=False cuando no se necesita una confirmación. Esto preserva la noción de transacción.

+0

@prosseek Parece que el método 'execute' de los cursores no puede ejecutar la instrucción 'commit'. Esto no es gran cosa porque puedes usar el método 'commit' en el objeto de conexión. – aaronasterling

Cuestiones relacionadas