Как удалить быстрее? - PullRequest
6 голосов
/ 17 июля 2009

У меня есть таблица БД, которая состоит из 2,5 миллиардов записей. Есть дубликаты на мелодию 11 миллионов. Какой самый быстрый способ удалить эти 11 миллионов записей?

Ответы [ 5 ]

20 голосов
/ 17 июля 2009

Удаление одного дубликата из множества - сложная задача, и с таким количеством записей возникает проблема.

Один из вариантов - перевернуть проблему с ног на голову и скопировать записи, которые вы хотите сохранить, в новую таблицу. Вы можете использовать синтаксис CREATE TABLE AS SELECT DISTINCT ... NOLOGGING, который будет копировать ваши дублирующиеся записи без использования журнала транзакций, что намного быстрее. После заполнения новой таблицы удалите / переименуйте старую и переименуйте новую на место.

См. http://www.databasejournal.com/features/oracle/article.php/3631361/Managing-Tables-Logging-versus-Nologging.htm

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

Мораль этой истории в том, что ... никогда используйте DELETE для избавления от большого количества записей, это ужасно медленно, потому что оно должно хранить все удаленные записи в журнале повторов. Либо копировать и переключать, либо TRUNCATE.

3 голосов
/ 17 июля 2009
DELETE
FROM    mytable
WHERE   rowid IN
        (
        SELECT  rowid
        FROM    (
                SELECT  rowid, ROW_NUMBER() OVER (ORDER BY dupfield) rn
                FROM    mytable r
                )
        WHERE   rn > 1
        )

или, может быть, даже это:

DELETE
FROM    mytable mo
WHERE   EXISTS
        (
        SELECT  NULL
        FROM    mytable mi
        WHERE   mi.dup_field = mo.dup_field
                AND mi.rowid <> mo.rowid
        )

Оба этих запроса будут использовать весьма эффективный HASH SEMI JOIN, последний будет быстрее, если на dup_field.

не будет индекса.

У вас может возникнуть желание скопировать строки, но учтите, что при копировании 2G строк будет генерироваться гораздо больше информации REDO и UNDO, чем при удалении 11M.

2 голосов
/ 22 июля 2009

Быстрее ли удалить существующие строки или создать правильную новую таблицу и удалить старую, зависит от множества факторов. 11 миллионов строк - это много, но это только 0,5% от общего числа строк в таблице. Вполне возможно, что воссоздание и удаление может быть намного медленнее, чем удаление, в зависимости от того, сколько индексов существует в исходной таблице, а также от того, где на страницах данных существуют строки, которые нужно удалить.

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

Наконец, почему необходимо, чтобы эта операция была "быстрой"? Это потому, что система должна быть отключена во время процесса? Вы могли бы написать процедуру, которая удаляет дубликаты, пока система активна, но не влияет на остальную часть системы с точки зрения использования отмены. Мы решили эту проблему в прошлом, сначала написав запрос, который собирает первичные ключи строк, которые будут удалены во второй таблице, например:

  INSERT
    INTO RowsToDeleteTable
  SELECT PKColumn
    FROM SourceTable
   WHERE <conditions used to find rows to remove>

CREATE UNIQUE INDEX PK_RowsToDelete ON RowsToDeleteTable (PKColumn);

Затем у нас есть блок PL / SQL, который перебирает строки в курсоре следующим образом:

BEGIN
  FOR theRow IN (SELECT PKColumn FROM RowsToDeleteTable ORDER BY 1) LOOP
    <delete source table for theRow.PKColumn)
    <optionally wait a bit>
    commit;
  END LOOP;
END;

или делает что-то вроде этого:

BEGIN
  FOR theRow IN (SELECT MIN(PKColumn) FROM RowsToDeleteTable ) LOOP
    <delete source table for theRow.PKColumn)
    <optionally wait a bit>
    DELETE RowsToDeleteTable
     WHERE PKColumn = theRow.PKColumn;
    commit;
  END LOOP;
END;

Циклы и «SELECT MAX», очевидно, менее эффективны, но они имеют то преимущество, что позволяют вам следить за ходом операции удаления. Мы поместили немного кода ожидания в цикл, чтобы позволить нам контролировать, насколько энергично происходит операция пожирания.

Первоначальное создание RowsToDeleteTable происходит очень быстро, и у вас есть преимущество, позволяющее процессу занять столько времени, сколько вы хотите. В этом случае «дыры», оставленные в ваших экстентах при удалении, не будут слишком плохими, поскольку вы удаляете такой небольшой процент от общего объема данных.

1 голос
/ 17 июля 2009

Сначала поместите индекс в столбец или столбцы, которые определяют и содержат повторяющиеся значения,

Затем, предположив, что таблица имеет первичный ключ (PK),

  Delete Table T Where PK <> 
        (Select Min(PK) From Table
         Where ColA = T.ColA
           ...  for each column in set defined above
           And ColB = T.ColB)

ПРИМЕЧАНИЕ: также можно использовать Max (PK), все, что вы делаете, это идентифицируете одну запись, которую не следует удалять из каждого набора дубликатов

РЕДАКТИРОВАТЬ: Чтобы исключить широкое использование журнала транзакций и раздела UNDO, вы можете сохранить значения, которые являются дублирующими, во временной таблице, а затем удалить дубликаты для каждой пары в одной транзакции ...

Предполагая, что только один столбец (назовите его ColA, число) определяет dupes ...

   Create Table Dupes (ColA Number)
   Insert Dupes(ColA)
   Select Distinct ColA
   From Table
   Group By ColA
   Having Count(*) > 1

   recordExists Number := 0 ;
   ColAValue Number;
   Select Case When Exists (Select Count(*) From Dupes)
   Then 1 Else 0 End Into recordExists From Dual;


   While recordExists = 1 
      Loop 
         Select (Select Max(ColA) From Dupes) 
         Into ColAValue From Dual;
         Begin Transaction
            Delete Table T
            Where ColA = ColAValue
               And pk <> (Select Min(Pk) From Table 
                          Where ColA = ColAValue);
            Delete Dupes Where ColA = ColAValue;
         Commit Transaction;
         Select Case When Exists (Select Count(*) From Dupes)
         Then 1 Else 0 End Into recordExists From Dual;
      End Loop;

Не проверено, поэтому синтаксис может потребовать массирования ...

0 голосов
/ 17 июля 2009

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

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

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