Мой код должен работать на разных версиях SQLite. Я использую расширение PHP SQLite3. На разных серверах в моей пользовательской базе установлены разные версии SQLite, самой ранней из которых является 3.7.
Мой код должен обновиться (обновить строку, вставить, если ее нет). Все завернуто в транзакции, а столбец name
TEXT имеет уникальный индекс. Я не могу сделать name
первичный ключ, потому что БЕЗ ROWID стал доступен только с версии 3.8.2, а мне нужна поддержка более ранних версий.
Я начал с подготовленного оператора upsert:
INSERT INTO tbl (name, value) VALUES (:n, :v)
ON CONFLICT(name) DO UPDATE SET value=excluded.value;
Это не работает до версии 3.24. Поэтому я переключился на два оператора с вызовом .changes() между ними. Псевдокод:
UPDATE tbl SET VALUE = :v WHERE name = :n;
if (0 === conn.changes()) {
INSERT INTO tbl (name, value) VALUES (:n, :v);
}
Является ли синтаксис «UPSERT… ON CONFLICT…» значительно быстрее? Стоит ли писать условный код, чтобы использовать его, когда позволяет версия SQLite? Или код с вызовом .changes() между операторами работает адекватно даже в более новых версиях? Я забочусь о хорошей производительности любой версии SQLite, доступной моему пользователю.
name
должен быть первичным ключом или иметь уникальный индекс.
UPSERT выполнит поиск по индексу, чтобы определить, присутствует ли уже :n
, а затем прочитает страницу из таблицы и запишет ту же страницу обратно (либо для обновления значения, либо для добавления новой строки). Если добавляется новая строка, индекс также необходимо обновить.
Комбинация UPDATE + INSERT также выполнит поиск по индексу, чтобы определить, присутствует ли уже :n
. Если это так, он прочитает страницу из таблицы и запишет ту же страницу обратно, чтобы обновить значение, как в случае с UPSERT.
Если это не так, то INSERT снова выполнит тот же поиск по индексу (который должен быть быстрым, поскольку нужные страницы, вероятно, будут в кеше), а затем выполнит вставку и обновление индекса, как и в случае с UPSERT.
Итак, единственное отличие — поиск по индексу (одна из самых быстрых операций) на страницах, которые уже должны быть в кеше, и только в том случае, если строка должна быть вставлена.
Это не должно быть существенной разницей, если только ваша таблица не содержит миллиарды записей.
Этот вопрос, вероятно, лучше подходит для сайта DBA, а не для SO.