Действительно ли возможно с помощью сценария/инструмента генерировать автоматически много операторов удаления на основе таблиц fk отношения, с помощью МН Oracle / SQL?
В примере: у Меня есть таблица: КУРИЦА (ЧИСЛО CHICKEN_CODE) и существует 30 таблиц с fk ссылками на его CHICKEN_CODE, который я должен удалить; существуют также другие 150 таблиц, связанных с внешним ключом с теми 30 таблицы, которые я должен удалить сначала.
Есть ли некоторый инструмент/сценарий PL/SQL, который я могу выполнить для генерации всех необходимых операторов удаления на основе отношений FK для меня?
(между прочим, я знаю о каскадном удалении на отношениях, но обратите внимание: Я не МОГУ ИСПОЛЬЗОВАТЬ IT В СВОЕЙ ПРОИЗВОДСТВЕННОЙ БАЗЕ ДАННЫХ, потому что это опасно!)
Я использую Oracle DataBase 10G R2.
Обратите внимание на это:
Генерировать оператор удаления от отношений внешнего ключа в SQL 2008?
Другой пользователь только что записал это в SQL-СЕРВЕРЕ 2008, кто-либо может преобразовать в Oracle 10G пуазейль / SQL? Я не могу... :-(
Предположите, что V_CHICKEN и V_NATION являются критериями для выбора КУРИЦЫ для удаления из корневой таблицы: условие: "где COD_CHICKEN = V_CHICKEN И COD_NATION = V_NATION" на корневой таблице.
(Мой первый ответ стал слишком длинным и трудным для редактирования, и он получил Wikified сообщества, что очень раздражает. Вот последняя версия скрипта. )
Этот сценарий пытается выполнить каскадное удаление посредством рекурсии. Следует избегать бесконечных циклов при наличии циклических ссылок. Но для этого требуется, чтобы все циклические ссылочные ограничения имели ON DELETE SET NULL
или ON DELETE CASCADE
.
CREATE OR REPLACE PROCEDURE delete_cascade(
table_owner VARCHAR2,
parent_table VARCHAR2,
where_clause VARCHAR2
) IS
/* Example call: execute delete_cascade('MY_SCHEMA', 'MY_MASTER', 'where ID=1'); */
child_cons VARCHAR2(30);
parent_cons VARCHAR2(30);
child_table VARCHAR2(30);
child_cols VARCHAR(500);
parent_cols VARCHAR(500);
delete_command VARCHAR(10000);
new_where_clause VARCHAR2(10000);
/* gets the foreign key constraints on other tables which depend on columns in parent_table */
CURSOR cons_cursor IS
SELECT owner, constraint_name, r_constraint_name, table_name, delete_rule
FROM all_constraints
WHERE constraint_type = 'R'
AND delete_rule = 'NO ACTION'
AND r_constraint_name IN (SELECT constraint_name
FROM all_constraints
WHERE constraint_type IN ('P', 'U')
AND table_name = parent_table
AND owner = table_owner)
AND NOT table_name = parent_table; -- ignore self-referencing constraints
/* for the current constraint, gets the child columns and corresponding parent columns */
CURSOR columns_cursor IS
SELECT cc1.column_name AS child_col, cc2.column_name AS parent_col
FROM all_cons_columns cc1, all_cons_columns cc2
WHERE cc1.constraint_name = child_cons
AND cc1.table_name = child_table
AND cc2.constraint_name = parent_cons
AND cc1.position = cc2.position
ORDER BY cc1.position;
BEGIN
/* loops through all the constraints which refer back to parent_table */
FOR cons IN cons_cursor LOOP
child_cons := cons.constraint_name;
parent_cons := cons.r_constraint_name;
child_table := cons.table_name;
child_cols := '';
parent_cols := '';
/* loops through the child/parent column pairs, building the column lists of the DELETE statement */
FOR cols IN columns_cursor LOOP
IF child_cols IS NULL THEN
child_cols := cols.child_col;
ELSE
child_cols := child_cols || ', ' || cols.child_col;
END IF;
IF parent_cols IS NULL THEN
parent_cols := cols.parent_col;
ELSE
parent_cols := parent_cols || ', ' || cols.parent_col;
END IF;
END LOOP;
/* construct the WHERE clause of the delete statement, including a subquery to get the related parent rows */
new_where_clause :=
'where (' || child_cols || ') in (select ' || parent_cols || ' from ' || table_owner || '.' || parent_table ||
' ' || where_clause || ')';
delete_cascade(cons.owner, child_table, new_where_clause);
END LOOP;
/* construct the delete statement for the current table */
delete_command := 'delete from ' || table_owner || '.' || parent_table || ' ' || where_clause;
-- this just prints the delete command
DBMS_OUTPUT.put_line(delete_command || ';');
-- uncomment if you want to actually execute it:
--EXECUTE IMMEDIATE delete_command;
-- remember to issue a COMMIT (not included here, for safety)
END;
Это отличное упражнение для развития ваших навыков PL / SQL и общих знаний Oracle!
Вам необходимо определить все ограниченные столбцы во всех таблицах с отношениями, производными от вашей главной таблицы. Вы можете получить всю необходимую информацию из двух представлений: ALL_CONSTRAINTS и ALL_CONS_COLUMNS . (Если все таблицы находятся в той же схеме, что и пользователь, выполняющий сценарий, вы можете использовать USER_CONSTRAINTS и USER_CONS_COLUMNS, если хотите)
Этот запрос найдет все ограничения внешнего ключа, которые ссылаются на данную таблицу ( CUSTOMER
в этом примере):
SELECT constraint_name, table_name, constraint_type
FROM all_constraints
WHERE constraint_type = 'R'
AND r_constraint_name IN (SELECT constraint_name
FROM all_constraints
WHERE constraint_type IN ('P', 'U')
AND table_name = 'CUSTOMER');
CONSTRAINT_NAME C
------------------------------ -
CUSTOMER_FK1 R
CUSTOMER_FK4 R
CUSTOMER_FK5 R
CUSTOMER_FK3 R
CUSTOMER_FK2 R
Теперь для каждого из результатов этого запроса вы можете использовать столбец CONSTRAINT_NAME
, чтобы получить имя таблицы и столбца, которые вы можете использовать для написания операторов DELETE чтобы удалить все дочерние строки во всех дочерних таблицах.
Этот пример получает имя таблицы и столбца для ограничения с именем CUSTOMER_FK1
SELECT table_name, column_name
FROM user_cons_columns
WHERE constraint_name = 'CUSTOMER_FK1'
TABLE_NAME COLUMN_NAME
----------------------------- ------------------------------------
RESERVATION CUSTOMER_UID
Вы могли бы сделать, например:
DELETE FROM reservation
WHERE customer_uid = 00153464
или
DELETE FROM reservation
WHERE customer_uid IN (SELECT customer_uid
FROM customer
WHERE customer_type = 'X')
Но ваши дочерние таблицы также имеют дочерние таблицы, поэтому, конечно, вы сначала придется удалить эти дочерние строки (назовем их внучатыми строками).Предположим, что существует таблица с именем reserve_detail, у которой есть отношения внешнего ключа с резервированием, ваша команда удаления для reserve_detail может выглядеть так:
DELETE FROM reservation_detail
WHERE reservation_uid in (SELECT reservation_uid
FROM reservation
WHERE customer_uid IN (SELECT customer_uid
FROM customer
WHERE customer_type = 'X')
И если у reserve_detail также есть дочерние элементы ... вы поняли. Конечно, вы можете использовать объединения вместо вложенных запросов, но принцип тот же: чем глубже идут ваши зависимости, тем сложнее становятся ваши команды удаления.
Итак, теперь вы знаете, как это сделать, задача состоит в том, чтобы написать общий сценарий PL / SQL для удаления всех дочерних строк, строк внуков, строк правнуков ... (до бесконечности) для любой данной таблицы из вверх дном. Вам нужно будет использовать рекурсию . Должна быть интересная программа для написания!
(Последнее изменение: сценарий был удален; окончательное решение см. В другом моем ответе.)
Проблема в том, что ключевой столбец верхнего уровня не распространяется до самого низа. Если вы можете выполнить УДАЛИТЬ ИЗ grandchild WHERE parent_id =: 1, это нормально. Если вам нужно сделать,
DELETE FROM grandchild
WHERE child_id in (SELECT id FROM child WHERE parent_id = :1)
, то при спуске на шесть или семь пунктов вы получите уродливые (и, вероятно, медленные) запросы.
Хотя вы сказали, что не можете сделать ограничения КАСКАДНЫМ, можете ли вы сделать их откладываемыми изначально немедленно? Это не повлияет на существующий код. Ваш сеанс удаления отложит все ограничения. Затем удалите из родительского элемента, удалите из дочернего элемента, где записи не было в родительском элементе, удалите из внука, где нет совпадения в дочернем элементе и т. Д.