Sqlite3 cursors live updating?

2019-02-27 09:27发布

问题:

Can someone please explain this to me:

import sqlite3

db = sqlite3.connect(':memory:')

db.execute('create table t1 (id integer primary key, val text)')
db.execute('create table t2 (id integer primary key, val text)')

c = db.cursor()
c.execute('insert into t1 values (?, ?)', (1, 'a'))
c.execute('insert into t2 values (?, ?)', (1, 'b'))
c.execute('insert into t1 values (?, ?)', (2, 'c'))
c.execute('insert into t2 values (?, ?)', (2, 'd'))

c.execute('''select t1.id, t1.val, t2.val
             from t1
             left join t2 using (id)
             where t1.id is not null

             union all

             select t2.id, t1.val, t2.val
             from t2
             left join t1 using (id)
             where t2.id is not null
             and t1.id is null

             ''')
for row in c:
    print(row[0])
    if row[0] == 1:
        c2 = db.cursor()
        c2.execute('delete from t1 where id = ?', (row[0],))

If I comment out the last three lines, the output is:

1
2

But if I uncomment the last three lines, the output is:

1
2
1

ie. the first cursor has been updated with the results of DML executed in the second cursor.

Is this expected behaviour? Is there some way to prevent it?

I'm running Python 3.6.3 (as per Ubuntu 17.10), in case that makes a difference.

回答1:

SQLite computes results rows on demand, if possible. But this is not always possible, so there is no guarantee.

You should never modify any table that you are currently reading in another query. (The database might scan the table in unobvious ways, so even changes to other rows might change the enumeration.)

If you intend to do such modifications, you have to read all rows before doing the changes, e.g., for row in c.fetchall(). Alternatively, read the table in single steps that re-search for the place where the last query left, i.e.:

SELECT ... FROM MyTable WHERE ID > :LastID ORDER BY ID LIMIT 1;