Вопрос

У нас есть запрос на удаление некоторых строк из таблицы на основе поля id (первичный ключ).Это довольно простой запрос:

delete all from OUR_TABLE where ID in (123, 345, ...)

Проблема в том, что идентификаторы no.of могут быть огромными (например.70k), поэтому запрос занимает много времени.Есть ли какой-нибудь способ оптимизировать это?(Мы используем sybase - если это имеет значение).

Это было полезно?

Решение

Подумайте о том, чтобы запускать это пакетно.Цикл, выполняющий 1000 записей одновременно, может быть намного быстрее, чем один запрос, который выполняет все, и, кроме того, не будет держать таблицу закрытой для других пользователей так долго.

Если у вас есть каскадное удаление (и затронуто множество таблиц внешних ключей) или задействованы триггеры, возможно, вам потребуется запускать еще меньшие пакеты.Вам придется испытать, чтобы понять, какое число лучше всего подходит для вашей ситуации.У меня были таблицы, которые мне приходилось удалять партиями по 100, и другие, где работало 50000 (в этом случае мне повезло, поскольку я удалял миллион записей).

Но в любом случае я бы поместил свои ключевые значения, которые я намереваюсь удалить, во временную таблицу и удалил оттуда.

Другие советы

Есть два способа сделать заявления, подобные этому, выполнить:

  1. Создайте новую таблицу и скопируйте все, кроме строк для удаления.После этого поменяйте местами таблицы (alter table name ...) Я предлагаю попробовать, даже если это звучит глупо.Некоторые базы данных копируются намного быстрее, чем удаляются.

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

Мне интересно, является ли проблемой синтаксический анализ предложения IN с 70 тысячами элементов в нем.Вы пробовали использовать временную таблицу с объединением вместо этого?

Может ли Sybase обработать 70 ТЫСЯЧ аргументов в предложении IN?Все базы данных, с которыми я работал, имеют некоторое ограничение на количество аргументов для IN оговорка.Например, у Oracle есть лимит около 1000.

Можете ли вы создать subselect вместо предложения IN?Это сократит sql.Возможно, это могло бы помочь для такого большого количества значений в предложении IN .Что- то вроде этого:

  DELETE FROM OUR_TABLE WHERE ID IN 
        (SELECT ID FROM somewhere WHERE some_condition)

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

  1. вы можете ускорить процесс, удаляя индексы, удаляя записи и создавая индексы заново.Это исключит перебалансировку деревьев индексов при удалении записей.

    • поместите все индексы в таблицу
    • удаление записей
    • воссоздание индексов
    • если у вас много связей с этой таблицей, попробуйте отключить ограничения, если вы абсолютно уверены, что команда delete не нарушит никаких ограничений целостности.Удаление пройдет намного быстрее, потому что база данных не будет проверять целостность.Включите ограничения после удаления.
    • отключить ограничения целостности, отключить ограничения проверки
    • удаление записей
    • включить ограничения
    • отключите триггеры в таблице, если они у вас есть и если это позволяют ваши бизнес-правила.Удалите записи, затем включите триггеры.

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

Я бы попробовал комбинацию 1, 2 и 3.Если это не сработает, то 4.Если все идет медленно, я бы поискал коробку побольше - больше памяти, более быстрые диски.

Узнайте, на что расходуется производительность!

Во многих случаях вы могли бы использовать одно из предоставленных решений.Но могут быть и другие (основанные на знаниях Oracle, поэтому в других базах данных все будет по-другому.Редактировать:только что увидел, что вы упомянули sybase):

  • У вас есть внешние ключи на этом столе?Удостоверяется, что ссылающиеся идентификаторы проиндексированы
  • У вас есть индексы в этой таблице?Возможно, удаление перед удалением и повторное создание после удаления может быть более быстрым.
  • проверьте план выполнения.Использует ли он индекс, где полное сканирование таблицы может быть быстрее?Или наоборот?ПОДСКАЗКИ могут помочь
  • вместо выбора в new_table, как было предложено выше, создать таблицу как select может быть еще быстрее.

Но помни:Сначала выясните, на что расходуется производительность.

При использовании инструкций DDL убедитесь, что вы понимаете и принимаете последствия, которые это может иметь для транзакций и резервных копий.

Попробуйте отсортировать идентификатор, который вы передаете в "in", в том же порядке, что и таблица, в которой хранится индекс.Затем вы можете получить больше обращений к дисковому кэшу.

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

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

Я также думаю, что временная таблица, вероятно, является лучшим решением.

Если бы вы должны были сделать "удалить из ..where ID in (выберите идентификатор из ...)" однако при больших запросах это все еще может быть медленным.Поэтому я предлагаю вам удалить с помощью соединения - многие люди не знают об этой функции.

Итак, приведем этот пример таблицы:

    -- set up tables for this example
    if exists (select id from sysobjects where name = 'OurTable' and type = 'U')
        drop table OurTable
    go

    create table OurTable (ID integer primary key not null)
    go
    insert into OurTable (ID) values (1)
    insert into OurTable (ID) values (2)
    insert into OurTable (ID) values (3)
    insert into OurTable (ID) values (4)
    go

Затем мы можем написать наш код удаления следующим образом:

    create table #IDsToDelete (ID integer not null)
    go
    insert into #IDsToDelete (ID) values (2)
    insert into #IDsToDelete (ID) values (3)
    go
    -- ... etc ...
    -- Now do the delete - notice that we aren't using 'from'
    -- in the usual place for this delete
    delete OurTable from #IDsToDelete
       where OurTable.ID = #IDsToDelete.ID
    go
    drop table #IDsToDelete
    go
    -- This returns only items 1 and 4
    select * from OurTable order by ID
    go

Есть ли в our_table ссылка на каскад удаления?

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top