Как генерировать Операторы удаления в МН / SQL, на основе отношений таблиц FK?

Действительно ли возможно с помощью сценария/инструмента генерировать автоматически много операторов удаления на основе таблиц 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" на корневой таблице.

6
задан Community 23 May 2017 в 12:10
поделиться

3 ответа

(Мой первый ответ стал слишком длинным и трудным для редактирования, и он получил 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;
21
ответ дан 8 December 2019 в 04:08
поделиться

Это отличное упражнение для развития ваших навыков 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 для удаления всех дочерних строк, строк внуков, строк правнуков ... (до бесконечности) для любой данной таблицы из вверх дном. Вам нужно будет использовать рекурсию . Должна быть интересная программа для написания!

(Последнее изменение: сценарий был удален; окончательное решение см. В другом моем ответе.)

2
ответ дан 8 December 2019 в 04:08
поделиться

Проблема в том, что ключевой столбец верхнего уровня не распространяется до самого низа. Если вы можете выполнить УДАЛИТЬ ИЗ grandchild WHERE parent_id =: 1, это нормально. Если вам нужно сделать,

DELETE FROM grandchild
WHERE child_id in (SELECT id FROM child WHERE parent_id = :1)

, то при спуске на шесть или семь пунктов вы получите уродливые (и, вероятно, медленные) запросы.

Хотя вы сказали, что не можете сделать ограничения КАСКАДНЫМ, можете ли вы сделать их откладываемыми изначально немедленно? Это не повлияет на существующий код. Ваш сеанс удаления отложит все ограничения. Затем удалите из родительского элемента, удалите из дочернего элемента, где записи не было в родительском элементе, удалите из внука, где нет совпадения в дочернем элементе и т. Д.

3
ответ дан 8 December 2019 в 04:08
поделиться
Другие вопросы по тегам:

Похожие вопросы: