SQLite: выберите результаты, затронутые последующей вставкой - PullRequest
3 голосов
/ 09 декабря 2011

Я занимаюсь разработкой приложения, в котором я перебираю множество (более 1 000 000) строк в таблице, одновременно вставляя новые строки и обновляя существующие строки.Требуется, чтобы оператор select выдавал каждую строку в таблице (те, которые существуют при первоначальном выполнении выбора) ровно один раз и никогда не приводил к строкам, которые вставляются после выполнения выбора.Я бы предпочел не загружать все строки в память (это занимает много времени и много оперативной памяти - я попробовал это).

Я разработал небольшой пример Python, который демонстрирует, что SQLite, очевидно, не изолирует вставки (и предположительно обновляет и удаляет) из длительной выборки.Мне не удалось найти места в документации по SQLite, где конкретно упоминается это поведение, но я нашел несколько ссылок, которые ссылались на тот факт, что вставка не удалась (возможно, в более ранних версиях SQLite?), Чего нет в моем примере.

import sqlite3

def select_affected_by_insert():
    # select from and simultaneously modify same table
    cn = sqlite3.connect(':memory:')
    cn.execute("CREATE TABLE demo (v INTEGER PRIMARY KEY)")

    n = 5
    values = [[v] for v in range(n)]
    cn.executemany('INSERT INTO demo VALUES (?)', values)

    for (v,) in cn.execute('SELECT v FROM demo'):

        with cn:
            # insert in transaction
            cn.execute('INSERT INTO demo VALUES (?)', [n + v])

        print v, n + v
        assert v < n, 'got more rows than expected!'

if __name__ == '__main__':
    select_affected_by_insert()

SQLite 3.6.12Python 2.6.4

Есть ли лучший способ обойти это, чем копировать данные в отдельную (временную) таблицу и выбирать из нее?

Уточнение: я забыл сказать, что мне нужноделать коммиты внутри цикла.Процесс может быть прерван, и частично выполненная работа должна быть зафиксирована, чтобы ее не нужно было повторять при следующем прогоне.

Ответы [ 2 ]

5 голосов
/ 09 декабря 2011
  1. Использование Режим WAL (чтобы устройство записи и чтения не мешали)
  2. Использование отдельных подключений для устройства чтения и записи
2 голосов
/ 09 декабря 2011

, если вы добавляете открытую базу данных в режиме отложенной транзакции и COMMIT в конце вашей логики SELECT - INSERT, например:

cn = sqlite3.connect(':memory:', isolation_level='DEFERRED')
...
for (v,) in cn.execute('SELECT v FROM demo'):
    cn.execute('INSERT INTO demo VALUES (?)', [n + v])
cn.commit()

Ваши операторы вставки должны быть отложеныдо конца блока.Из документов SQLite для управления транзакциями :

Если одновременно выполняется несколько команд для одного и того же соединения с базой данных SQLite, автоматическая фиксация откладывается до завершения самой последней команды,Например, если выполняется инструкция SELECT, выполнение команды приостанавливается, поскольку возвращается каждая строка результата.Во время этой паузы другие команды INSERT, UPDATE или DELETE могут быть выполнены для других таблиц в базе данных. Но ни одно из этих изменений не будет зафиксировано, пока не завершится исходный оператор SELECT.

...