SQLite вставляет или игнорирует и возвращает исходный rowid

Я провел некоторое время, читая SQLite docs различные вопросы и ответы здесь, на переполнение стека, и эта штука, но не пришли к полному ответу.

Я знаю, что нет никакого способа, чтобы сделать что-то вроде INSERT OR IGNORE INTO foo VALUES(...) С SQLite и вернуть rowid исходной строки, и что ближе всего к нему будет INSERT OR REPLACE но это удаляет всю строку и вставляет новую строку и таким образом получает новый идентификатор rowid.

пример таблица:

CREATE TABLE foo(
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    data TEXT
);

прямо сейчас я могу сделать:

sql = sqlite3.connect(":memory:")
# create database
sql.execute("INSERT OR IGNORE INTO foo(data) VALUES(?);", ("Some text.", ))
the_id_of_the_row = None
for row in sql.execute("SELECT id FROM foo WHERE data = ?", ("Some text.", )):
    the_id_of_the_row = row[0]

но что-то идеально будет выглядеть так:

the_id_of_the_row = sql.execute("INSERT OR IGNORE foo(data) VALUES(?)", ("Some text", )).lastrowid

каков наилучший (читай: самый эффективный) способ вставить строку в таблицу и вернуть rowid или проигнорировать строку, если она уже существует, и просто получить rowid? эффективность важна, потому что это будет происходить довольно часто.

есть ли способ INSERT OR IGNORE и возвращает rowid строки, которую игнорируется Роу сравнивали с? это было бы здорово, так как это было бы так же эффективно, как вставка.

2 ответов


способ, который работал лучше всего для меня было insert or ignore значения, и select rowid в два отдельных шага. Я использовал unique ограничение data столбец для ускорения выбирает и избегает дубликатов.

sql.execute("INSERT OR IGNORE INTO foo(data) VALUES(?);" ("Some text.", ))
last_row_id = sql.execute("SELECT id FROM foo WHERE data = ?;" ("Some text. ", ))

на select утверждение не так медленно, как я думал. Это кажется, из-за SQLite автоматически создает индекс для unique столбцы.


INSERT OR IGNORE для ситуаций, когда вы не заботитесь о идентичности записи; где цель состоит только в том, чтобы иметь некоторые записи с конкретным значением.

если вы хотите знать, вставлена ли новая запись или нет, вы должны проверить вручную:

the_id_of_the_row = None
for row in sql.execute("SELECT id FROM foo WHERE data = ?", ...):
    the_id_of_the_row = row[0]
if the_id_of_the_row is None:
    c = sql.cursor()
    c.execute("INSERT INTO foo(data) VALUES(?)", ...)
    the_id_of_the_row = c.lastrowid

что касается эффективности: когда SQLite проверяет dataстолбец для дубликатов, он должен делать точно такой же запрос, который вы делаете с SELECT, и как только вы это сделаете, путь доступа в кэше, поэтому производительность не должны быть проблемой. Во всяком случае, это is необходимо выполнить два отдельных INSERT/SELECT запросы (в любом порядке, как ваш, так и мой код работают, но ваш проще).