Перемещение строк между таблицами в SQL

У меня есть 2 таблицы, активная таблица и неактивная таблица. Я хочу переместить строки из активной таблицы в неактивную. Моя первая мысль была

insert into inactive select * from active where ...
delete from active active where ...

Однако примерно через 0,42 секунды я заметил, что это приведет к удалению/дублированию строк, если обновления изменят то, что выбирает предложение where.

В этом случае я могу легко предотвратить это, но что мне делать в тех случаях, когда я не могу?

редактировать: из ответов похоже, что нет простого/тривиального способа сделать это. Я действительно удивлен этим. Я бы подумал, что в этом есть существенная польза.


person BCS    schedule 18.11.2008    source источник


Ответы (8)


Флаги состояния — ваш друг.

UPDATE old_data SET move="MARKED";
INSERT INTO somewhere... SELECT where move="MARKED";
DELETE FROM old_data WHERE move="MARKED";

Если вы сделаете это с выключенным Autocommit, он будет захватывать блокировки повсюду.

Вы можете COMMIT после каждого шага, если вы хотите сделать немного меньше блокировки.

person S.Lott    schedule 18.11.2008
comment
Вроде для этого нужен FTS3? Я получил Error: no such module: FTS3. Я не могу найти двоичный файл sqlite3 для Android с FTS3... - person Luis A. Florit; 22.10.2015

(По крайней мере, в MS SQL) вы можете использовать транзакции и подсказки блокировки:

begin tran

insert into inactive
select * from active with (updlock)
where ...

delete from active
where ...

commit tran

Без подсказки блокировки (updlock) могут возникнуть взаимоблокировки, если кто-то изменит удаляемые записи.

person Arvo    schedule 18.11.2008
comment
Извините, я ничего не знаю о sqlite, но получаю Error: near "(": syntax error - person Luis A. Florit; 22.10.2015
comment
@LuisA.Florit - где я сказал «sqlite»? Мой совет будет работать в MS SQL Server, скорее всего, в Oracle и других sql-серверах есть аналогичные возможности, но синтаксис может отличаться. SQLite имеет несколько урезанную функциональность; поддерживает ли он транзакции и подсказки запросов, я не знаю. Из вашего комментария я бы сказал, что подсказки не поддерживаются. - person Arvo; 23.10.2015

Поддерживает ли ваша база данных условие OUTPUT? Тогда это может быть идеальным и простым решением для вас, не так ли?

http://msdn.microsoft.com/en-us/library/ms177564.aspx

delete active with (readpast)
output DELETED.*
into inactive
where ...
person Örjan Jämte    schedule 19.11.2008
comment
Я должен проверить, но если это так, это ИМЕННО то, что я искал!! (MySQL на случай, если кто-то доберется до него раньше меня.) - person BCS; 20.11.2008
comment
Что насчет триггеров? В SQL Server я выполнил аналогичную задачу с триггером удаления, который вставлял удаленные сообщения в таблицу истории. - person Örjan Jämte; 20.11.2008

Если вы можете блокировать строки пессимистично, то есть, когда вы их читаете, чтобы скопировать их в неактивную таблицу, то никто не может изменить их из-под вас.

Способ блокировки строк зависит от марки базы данных, и вы не указываете это в своем вопросе.

person Bill Karwin    schedule 18.11.2008

Объявите локальную таблицу var и поместите туда значения, вставьте их в свою неактивную таблицу, затем удалите строки из активной таблицы, которые находятся в вашем локальном списке.

Идея транзакции работает так же хорошо.

person Nick DeVore    schedule 18.11.2008

Либо используйте уникальный столбец идентификатора, например

delete from active where rowid in (select rowid in inactive)

or

delete from active as a 
where exists (select * 
              from inactive 
              where pkfld1=a.pkfld1 
              and pkfld2=a.pkfld2)

Также не забудьте обернуть этот процесс в транзакцию.

Удачи.

person John MacIntyre    schedule 18.11.2008
comment
EXISTS не полностью специфичен для MS SQL Server - это стандарт SQL-92, и я полагаю, что он также используется в MySQL и Oracle (кто-нибудь поправит меня, если я ошибаюсь). - person Ian Varley; 19.11.2008
comment
хм... похоже вы правы. contrib.andrew.cmu.edu/~shadow/sql/sql1992. текст - person John MacIntyre; 19.11.2008

Вот как я сохраняю свои предложения 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)

Если вам нужно сильнее этого, вам нужно посмотреть на блокировку (блокировать изменения во время транзакции).

person Amy B    schedule 18.11.2008

Почему у вас есть две таблицы, а не столбец для «IsActive»?

person dkretz    schedule 18.11.2008
comment
Производительность, связанная с размером таблицы. IIRC только это может быть хорошей причиной. В моих случаях из-за того, как я его использую, если запрос делает что-либо / кроме / поиска по индексу, он / превратится / превратится в поиск в худшем случае (вроде как здесь en.wikipedia.org/wiki/Linear_probing) - person BCS; 19.11.2008
comment
Кроме того: я никогда больше не буду использовать этот код, так зачем исправлять то, что в основном работает ;-) - person BCS; 19.11.2008
comment
Хорошо, согласен. Сейчас мы обсуждаем одно из последствий. Другой неизбежен поиск записей ГДЕ ... и они могут быть в любой таблице. Резонный вопрос может заключаться в том, что это лучший способ справиться с проблемами производительности, связанными с размером таблицы? Вот почему я спрашиваю. - person dkretz; 19.11.2008
comment
Является ли таблица настолько огромной, что SQL Server фактически не может достаточно эффективно хранить и извлекать индексы? Представленный вопрос не предполагает такого уровня понимания технологии. - person dkretz; 19.11.2008