Проблема удаления дублейИсточник: oracle
Автор: Виктор Абрамов, IT-дирекция
SQL> DELETE FROM tab
WHERE ROWID IN (SELECT ROWID FROM tab MINUS SELECT MIN (ROWID) FROM tab GROUP BY c1, c2, c3); Мы не будем дальше останавливаться на этой задаче, поскольку совсем не этот аспект является темой настоящей статьи. Более того, темой статьи не является даже и отыскание дублей. Нашей проблемой является удаление повторяющихся записей, на которые есть ссылки из других таблиц. Сами повторяющиеся строки известны заранее. Очень часто оказывается, что пользователи не только несколько раз ввели одну и ту же запись в таблицу, но и успели несколько раз на нее сослаться из других таблиц, благо, приложение это позволяло. Например, не посмотрев, что некая компания уже имеется в списке контрагентов, пользователь внес ее в справочник контрагентов еще раз, причем немного ошибся в названии, так что отследить дубль по названию не представлялось возможным. Затем он завел список контактных лиц, заполнил справочник синонимов названия компании, и зарегистрировал парочку заказов. Впоследствии оказалось, что предприятие давно работает с данной компанией, и уже выполнило для нее несколько десятков заказов. И вот теперь нам нужно удалить из справочника компаний лишнюю запись, переключив предварительно все ссылки на нее на ту запись в таблице, которая остается. Конечно, учет психологии пользователя при разработке интерфейса приложения может уменьшить количество таких дублей, но я не знаю абсолютно надежного способа предотвратить подобное поведение пользователей. Ручное же лечение проблемы занимает слишком много драгоценного времени администратора приложения. Если подобная проблема возникает только с одной таблицей, можно написать хранимую процедуру именно под нее, но что делать, если таких таблиц несколько? Эта проблема может возникнуть и не из-за ошибок пользователя, а, например, при слиянии подразделений, когда необходимо всех сотрудников перевести из одного подразделения в другое. Итак, попытаемся разработать универсальную процедуру для удаления повторяющихся строк в любых таблицах, на которые есть ссылки. Для упрощения задачи наложим следующие ограничения:
Второе ограничение очень важно. Если Ваша база данных разработана недоучками, не знающими, зачем нужны первичные и внешние ключи, то эта статья Вам не поможет. Сначала извлечем нужную нам информацию из словаря данных на примере таблицы подразделений DEPARTMENTS.
Зная идентификатор подразделения, которое нужно оставить (correct_id) и идентификатор удяляемого подразделение (drop_id), мы можем, используя имена таблиц и столбцов внешнего ключа, полученные из второго запроса, сформировать запрос на изменение значений внешнего ключа. А из третьего запроса - получить SQL - команду на удаление ставшей ненужной строки из таблицы DEPARTMENTS. Но мы пойдем немного дальше, и создадим хранимую процедуру, которой достаточно будет передать три параметра - имя таблицы, правильное и удаляемое значения первичного ключа, и она все сделает за нас. Чтобы не задумываться над типом столбца первичного ключа, создадим две идентичных процедуры с одинаковыми именами, используя принцип перегрузки, "засунем" их в один хранимый пакет, и назовем его DOUBLES (см. Приложение 1 ). Теперь, чтобы выполнить распоряжение директора о слиянии бухгалтерии (DEPT_ID = 28) и финансового отдела (DEPT_ID = 101), достаточно выполнить команду:
SQL> SET SERVEROUTPUT ON
SQL> EXECUTE DOUBLES.DEL('DEPARTMENTS',28,101) DEFAULT_COORDGROUPS - изменено строк: 4 DEFAULT_GROUPS - изменено строк: 2 DEFAULT_ROLES - изменено строк: 4 EMPLOYEES - изменено строк: 17 DEPARTMENTS - удалено строк: 1 PL/SQL procedure successfully completed. Приложение 1
CREATE OR REPLACE
PACKAGE doubles IS PROCEDURE del (tname IN VARCHAR2, correct_id IN NUMBER, drop_id IN NUMBER); PROCEDURE del (tname IN VARCHAR2, correct_id IN VARCHAR2, drop_id IN VARCHAR2); CREATE OR REPLACE PROCEDURE del (tname IN VARCHAR2, correct_id IN VARCHAR2, drop_id IN VARCHAR2) FOR r IN c_delete_double (tname) DBMS_OUTPUT.put_line (r.table_name // ' - удалено строк: ' // SQL%ROWCOUNT); |