У меня есть 2 таблицы, активная таблица и неактивная таблица. Я хочу, чтобы строки переехать из активной таблицы в неактивную. Моя первая мысль была
insert into inactive select * from active where ...
delete from active active where ...
Однако примерно через 0,42 секунды я заметил, что это приведет к удалению / дублированию строк, если обновления изменят то, что выбирает предложение where.
В этом случае я легко могу это предотвратить, но что мне делать, если я не могу?
edit: Из ответов похоже, что нет простого / тривиального способа сделать это. Я действительно удивлен этим. Я думаю, что это принесет существенные выгоды.


Флаги статуса - ваш друг.
UPDATE old_data SET move = "MARKED";
INSERT INTO somewhere... SELECT where move = "MARKED";
DELETE FROM old_data WHERE move = "MARKED";
Если вы сделаете это с выключенным Autocommit, он повсюду захватит блокировки.
Вы можете COMMIT после каждого шага, если хотите немного меньше блокировать.
(По крайней мере, в MS SQL) вы можете использовать транзакции и подсказки блокировки:
begin tran
insert into inactive
select * from active with (updlock)
where ...
delete from active
where ...
commit tran
Без указания блокировки (обновления) могут возникнуть взаимоблокировки, если кто-то изменит удаляемые записи.
Извините, я ничего не знаю о sqlite, но у меня Error: near "(": syntax error
@ LuisA.Florit - где я сказал "sqlite"? Мой совет будет работать в MS SQL Server, скорее всего, есть аналогичные возможности в Oracle и других серверах sql, но синтаксис может отличаться. SQLite имеет несколько ограниченную функциональность; поддерживает ли он транзакции и подсказки запросов, я не знаю. Из вашего комментария я бы сказал, что подсказки не поддерживаются.
Если вы можете заблокировать строки пессимистически, то есть когда вы читаете их, чтобы скопировать их в неактивную таблицу, тогда никто не сможет изменить их из-под вас.
Метод блокировки строк зависит от марки базы данных, и вы не указываете это в своем вопросе.
Объявите локальную таблицу var и поместите в нее значения, вставьте их в неактивную таблицу, затем удалите строки из активной таблицы, которые находятся в вашем локальном списке.
Однако идея транзакции тоже работает.
Либо используйте уникальный столбец идентификатора, например
delete from active where rowid in (select rowid in inactive)
или же
delete from active as a
where exists (select *
from inactive
where pkfld1=a.pkfld1
and pkfld2=a.pkfld2)
Также не забудьте обернуть этот процесс транзакцией.
Удачи.
EXISTS не совсем специфичен для MS SQL Server - это стандарт SQL-92, и я верю, что он также используется в MySQL и Oracle (кто-нибудь поправит меня, если я ошибаюсь).
хммм ... похоже, вы правы. contrib.andrew.cmu.edu/~shadow/sql/sql1992.txt
Вот как я сохраняю свои предложения where:
DECLARE @MyTable TABLE
(
TheKey int PRIMARY KEY
)
--
INSERT INTO @MyTable(TheKey)
SELECT TheKey FROM SourceTable WHERE rows I want
--
INSERT INTO Inactive(fieldlist)
SELECT fieldlist
FROM Active
WHERE TheKey IN (SELECT TheKey FROM @MyTable)
--
DELETE
FROM Active
WHERE TheKey IN (SELECT TheKey FROM @MyTable)
Если вам нужно более сильное, вам нужно посмотреть на блокировку (заблокировать изменения во время транзакции).
Почему у вас две таблицы, а не столбец для IsActive?
Производительность, связанная с размером таблицы. Это одно только IIRC может быть хорошей причиной.В моих случаях из-за того, как я его использую, если запрос делает что-либо, но / поиск по индексу, он / превратится в поиск наихудших случаев (вроде как здесь en.wikipedia.org/wiki/Linear_probing )
Также: «Я никогда больше не буду использовать этот код, так зачем исправлять то, что в основном работает» ;-)
ОК, согласился. Теперь мы обсуждаем одно из последствий. Другой - неизбежный «найти записи ГДЕ ...», и они могут быть в любой таблице. Возникает резонный вопрос: «Оказывается, это лучший способ справиться с проблемами производительности, связанными с размером таблицы?» Вот почему я спрашиваю.
Неужели таблица настолько огромна, что SQL Server не может достаточно эффективно хранить и извлекать индексы? Представленный вопрос не предполагает такого глубокого понимания технологии.
Поддерживает ли ваша база данных пункт OUTPUT? Тогда это могло бы быть идеальным и простым решением для вас, не так ли?
http://msdn.microsoft.com/en-us/library/ms177564.aspx
delete active with (readpast)
output DELETED.*
into inactive
where ...
Я должен буду проверить, но если это произойдет, это ТОЧНО то, что я искал !! (MySQL на случай, если кто-то доберется до меня раньше, чем я.)
А как насчет триггеров? В SQL Server я выполнил аналогичную задачу с триггером на удаление, который вставлял удаленные сообщения в таблицу истории.
Вроде для этого нужен FTS3? Получил
Error: no such module: FTS3. Я не могу найти двоичный файл sqlite3 для Android с FTS3 ...