Улучшение производительности Sql Delete - PullRequest
6 голосов
/ 23 февраля 2009

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

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

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

Ответы [ 8 ]

4 голосов
/ 23 февраля 2009

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

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

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

3 голосов
/ 23 февраля 2009

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

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

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

3 голосов
/ 23 февраля 2009

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

2 голосов
/ 23 февраля 2009

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

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

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

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

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

2 голосов
/ 23 февраля 2009

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

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

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

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

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

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

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

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

1 голос
/ 09 марта 2011

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

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

Итак, с учетом этого примера таблицы:

    -- 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
1 голос
/ 23 февраля 2009

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

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

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

0 голосов
/ 23 февраля 2009

Имеет ли our_table ссылку на каскад удаления?

...