SQL удалить сироту - PullRequest
       9

SQL удалить сироту

3 голосов
/ 08 января 2010

Предполагая, что все внешние ключи имеют соответствующее ограничение, существует ли простая инструкция SQL для удаления строк, на которые нет ссылок нигде в БД?

Что-то простое, как delete from the_table, которое просто пропускает строки с дочерней записью?

Я пытаюсь избежать ручного обхода таблицы или добавления чего-то вроде where the_SK not in (a,b,c,d).

Ответы [ 4 ]

6 голосов
/ 08 января 2010

Возможно, вы сможете использовать расширенный оператор DELETE в 10g, который включает в себя ведение журнала ошибок.

Сначала используйте DBMS_ERRLOG для создания таблицы журналов (которая является просто копией исходной таблицы с некоторыми дополнительными столбцами префиксов: ORA_ERR_MESG$, ..., ORA_ERR_TAG$)

execute dbms_errlog.create_error_log('parent', 'parent_errlog');

Теперь вы можете использовать предложение LOG ERRORS оператора delete для захвата всех строк, которые имеют существующие ограничения целостности:

delete from parent
   log errors into parent_errlog ('holding-breath')
   reject limit unlimited;

В этом случае комментарий «задержка дыхания» будет помещен в столбец ORA_ERR_TAG$.

Вы можете прочитать полную документацию здесь .

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

  1. Прямая ссылка на дочерние таблицы (в соответствии с решением Тони ) или
  2. Перебрать таблицу в PL / SQL и перехватить любые исключения (следуя Путанице и Бобу ).
2 голосов
/ 08 января 2010

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

1 голос
/ 08 января 2010

Один из способов сделать это - написать что-то вроде следующего:

eForeign_key_violation EXCEPTION;
PRAGMA EXCEPTION_INIT(eForeign_key_violation, -2292);

FOR aRow IN (SELECT primary_key_field FROM A_TABLE) LOOP
  BEGIN
    DELETE FROM A_TABLE A
    WHERE A.PRIMARY_KEY_FIELD = aRow.PRIMARY_KEY_FIELD;
  EXCEPTION
    WHEN eForeign_key_violation THEN
      NULL;  -- ignore the error
  END;
END LOOP;

Если существует дочерняя строка, УДАЛИТЬ не удастся и строки не будут удалены, и вы можете перейти к следующему ключу.

Обратите внимание, что если ваш стол большой, это может занять довольно много времени.

1 голос
/ 08 января 2010

Нет. Очевидно, что вы можете сделать это (но я понимаю, что вы бы предпочли этого не делать):

delete parent
where  not exists (select null from child1 where child1.parent_id = parent.parent_id)
and    not exists (select null from child2 where child2.parent_id = parent.parent_id)
...
and    not exists (select null from childn where childn.parent_id = parent.parent_id);
...