2

MySQL 데이터베이스에서 Constraints를 사용합니다. 하지만 이제 다른 항목에 외래 키 관계가있는 항목을 삭제하려고하면 두통이 생깁니다. 이 재귀 적 내가 삭제하기 위해 노력하고있어 행에 대한 외래 키 관계가있는 모든 행을 삭제하도록MySQL에서 행을 재귀 적으로 삭제하는 방법 (즉, 외래 키 링크 된 행을 삭제하는 방법)

Cannot delete or update a parent row: a foreign key constraint fails 

내가, 삭제 문 어떤 매개 변수 또는 아무것도 전달할 수 있습니다 : 난 항상이 오류가? 이것

답변

0

봐 :

In what order are ON DELETE CASCADE constraints processed?

하지만 난 당신이 내 연구에서 DELETE CASCADE ON 사용할 수 있다고 생각합니다. 내가 틀렸다면 커뮤니티가 알려줄 것이라고 확신합니다. 가능하다면 당신이 당신의 테이블을 바꿔야한다고 믿습니다.

또한이를 참조하십시오

UPDATE

Cannot delete or update a parent row: a foreign key constraint fails

+0

나는 그가 'DELETE CASCADE'스키마 변경이 아니라'delete' 문의 매개 변수/변형을 요청했다고 생각합니다. –

0

: 나는 반복적에서 삭제하는 저장 프로 시저를 작성했습니다 https://stevettt.blogspot.co.uk/2018/02/how-to-automate-deletion-of-rows-in.html


: 지금 블로그 게시물에이를 만든 적이 모든 외부 키 링크 테이블 (외래 키 검사를 끄거나 계단식 삭제를 켜지 않고). 구현은 다소 복잡하지만 "블랙 박스"로 취급 될 수 있습니다. 간단히 스키마 (데이터베이스), 테이블 및 WHERE 절의 이름을 지정하여 삭제할 레코드를 제한하고 나머지는 수행합니다.

데모

Rextester 온라인 데모 : http://rextester.com/MDMRA15991

SQL

-- ------------------------------------------------------------------------------------ 
-- USAGE 
-- ------------------------------------------------------------------------------------ 
-- CALL delete_recursive(<schema name>, <table name>, <WHERE clause>, <delete flag>); 
-- where: 
-- <schema name> is the name of the MySQL schema 
-- <table name> is the name of the base table to delete records from 
-- <WHERE clase> is a SQL WHERE clause to filter which records that are to be deleted 
-- <delete flag> is either TRUE or FALSE: If TRUE, the records *will* be deleted. 
--    If FALSE, the SQL will be output without actually deleting anything. 
-- Example: 
-- CALL delete_recursive('mydb', 'mytable', 'WHERE mypk IN (1, 2, 3)', TRUE); 
DROP PROCEDURE IF EXISTS delete_recursive; 
DELIMITER // 
CREATE PROCEDURE delete_recursive(schema_name VARCHAR(64), 
            tbl_name VARCHAR(64), 
            where_clause TEXT, 
            do_delete BIT) 
BEGIN 
    DECLARE next_schema_name, next_tbl_name VARCHAR(64); 
    DECLARE from_clause, next_where_clause, next_col_names, ref_col_names TEXT; 
    DECLARE done INT DEFAULT FALSE; 
    DECLARE cursor1 CURSOR FOR 
    SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAMES, REF_COLUMN_NAMES FROM temp_kcu; 
    DECLARE cursor2 CURSOR FOR 
    SELECT table_schema, table_name, where_sql FROM temp_deletes ORDER BY id; 
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; 

    -- Set maximum recursion depth 
    SET @@SESSION.max_sp_recursion_depth = 255; 

    -- Increment current recursion depth since the stored procedure has been entered. 
    SET @recursion_depth = IFNULL(@recursion_depth + 1, 0); 

    -- Create temporary table for storing the deletes if it doesn't already exist 
    IF @recursion_depth = 0 THEN 
    DROP TEMPORARY TABLE IF EXISTS temp_deletes; 
    CREATE TEMPORARY TABLE temp_deletes (
     id INT NOT NULL AUTO_INCREMENT, 
     table_schema VARCHAR(64), 
     table_name VARCHAR(64), 
     where_sql TEXT, 
     Notes TEXT, 
     PRIMARY KEY(id) 
    ); 
    END IF; 

    -- Construct FROM clause (including the WHERE clause) for this table. 
    SET from_clause = 
    CONCAT(' FROM ', schema_name, '.', tbl_name, ' WHERE ', where_clause); 

    -- Find out whether there are any foreign keys to this table 
    SET @query = CONCAT('SELECT COUNT(*) INTO @count', from_clause); 
    PREPARE stmt FROM @query; 
    EXECUTE stmt; 
    DEALLOCATE PREPARE stmt; 

    IF @count > 0 THEN 
    -- There are foriegn keys to this table so all linked rows must be deleted first: 
    -- Firstly, fill a temporary table with the foreign key metadata. 
    DROP TEMPORARY TABLE IF EXISTS temp_kcu; 
    SET @query = CONCAT(
     'CREATE TEMPORARY TABLE temp_kcu AS ', 
     'SELECT TABLE_SCHEMA, TABLE_NAME, ', 
     'GROUP_CONCAT(CONCAT(COLUMN_NAME) SEPARATOR '', '') AS COLUMN_NAMES, ', 
     'GROUP_CONCAT(CONCAT(REFERENCED_COLUMN_NAME) SEPARATOR '', '') 
     AS REF_COLUMN_NAMES ', 
     'FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE ', 
     'WHERE REFERENCED_TABLE_SCHEMA = ''', schema_name, 
     ''' AND REFERENCED_TABLE_NAME = ''', tbl_name, ''' ', 
     'GROUP BY CONSTRAINT_NAME'); 
    PREPARE stmt FROM @query; 
    EXECUTE stmt; 
    DEALLOCATE PREPARE stmt; 

    -- Loop through all foreign keys to this table using a cursor. 
    OPEN cursor1; 
    read_loop: LOOP 
     FETCH cursor1 INTO next_schema_name, next_tbl_name, next_col_names, 
      ref_col_names; 
     IF done THEN 
     -- No more rows so exit the loop. 
     LEAVE read_loop; 
     END IF; 

     -- Recursively call the stored procedure to delete linked rows 
     -- for this foreign key. 
     IF INSTR(next_col_names, ',') = 0 THEN 
     SET next_where_clause = CONCAT(
      next_col_names, ' IN (SELECT ', ref_col_names, from_clause, ')'); 
     ELSE 
     SET next_where_clause = CONCAT(
      '(', next_col_names, ') IN (SELECT ', ref_col_names, from_clause, ')'); 
     END IF; 
     CALL delete_recursive(
     next_schema_name, next_tbl_name, next_where_clause, do_delete); 
    END LOOP; 
    CLOSE cursor1; 
    END IF; 

    -- Build the DELETE statement 
    SET @query = CONCAT(
    'DELETE FROM ', schema_name, '.', tbl_name, ' WHERE ', where_clause); 

    -- Get the number of primary key columns 
    SET @pk_column_count = (SELECT COUNT(*) 
          FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE 
          WHERE TABLE_SCHEMA = schema_name 
          AND TABLE_NAME = tbl_name 
          AND CONSTRAINT_NAME = 'PRIMARY'); 
    IF @pk_column_count = 0 THEN 
    -- No primary key so just output the number of rows to be deleted 
    SET @query = CONCAT(
     'SET @notes = CONCAT(''No primary key; number of rows to delete = '', 
     (SELECT COUNT(*) FROM ', schema_name, '.', tbl_name, ' WHERE ', 
     where_clause, '))'); 
    PREPARE stmt FROM @query; 
    EXECUTE stmt; 
    DEALLOCATE PREPARE stmt; 
    ELSEIF @pk_column_count = 1 THEN 
    -- 1 primary key column. 
    -- Output the primary keys of the records to be deleted 
    SET @pk_column = (SELECT COLUMN_NAME 
         FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE 
         WHERE TABLE_SCHEMA = schema_name 
         AND TABLE_NAME = tbl_name 
         AND CONSTRAINT_NAME = 'PRIMARY'); 
    SET @pk_column_csv = CONCAT('CONCAT('''''''', ', @pk_column, ', '''''''')'); 
    SET @query = CONCAT(
     'SET @notes = (SELECT CONCAT(''', @pk_column, ' IN ('', GROUP_CONCAT(', 
     @pk_column_csv, ' SEPARATOR '', ''), '')'') FROM ', 
     schema_name, '.', tbl_name, ' WHERE ', where_clause, ')'); 
    PREPARE stmt FROM @query; 
    EXECUTE stmt; 
    DEALLOCATE PREPARE stmt; 
    ELSE 
    -- Multiple primary key columns. 
    -- Output the primary keys of the records to be deleted. 
    SET @pk_columns = (SELECT GROUP_CONCAT(COLUMN_NAME SEPARATOR ', ') 
         FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE 
         WHERE TABLE_SCHEMA = schema_name 
         AND TABLE_NAME = tbl_name 
         AND CONSTRAINT_NAME = 'PRIMARY'); 
    SET @pk_columns_csv = (SELECT CONCAT('CONCAT(''('''''', ', GROUP_CONCAT(COLUMN_NAME 
          SEPARATOR ', '''''', '''''', '), ', '''''')'')') 
          FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE 
          WHERE TABLE_SCHEMA = schema_name 
          AND TABLE_NAME = tbl_name 
          AND CONSTRAINT_NAME = 'PRIMARY');  
    SET @query = CONCAT(
    'SET @notes = (SELECT CONCAT(''(', @pk_columns, 
    ') IN ('', GROUP_CONCAT(', @pk_columns_csv, ' SEPARATOR '', ''), '')'') FROM ', 
     schema_name, '.', tbl_name, ' WHERE ', where_clause, ')'); 
    PREPARE stmt FROM @query; 
    EXECUTE stmt; 
    DEALLOCATE PREPARE stmt; 
    END IF; 

    IF @notes IS NULL THEN 
    SET @notes = 'No affected rows.'; 
    END IF; 

    -- Save details of the DELETE statement to be executed 
    INSERT INTO temp_deletes (table_schema, table_name, where_sql, Notes) 
    VALUES (schema_name, tbl_name, where_clause, @notes); 

    IF @recursion_depth = 0 THEN 
    -- Output the deletes. 
    SELECT CONCAT('DELETE FROM ', schema_name, '.', table_name, 
        ' WHERE ', where_sql) `SQL`, 
      Notes 
    FROM temp_deletes ORDER BY id; 

    IF do_delete THEN 
     -- Perform the deletes: Loop through all delete queries using a cursor. 
     SET done = FALSE; 
     OPEN cursor2; 
     read_loop: LOOP 
     FETCH cursor2 INTO schema_name, tbl_name, where_clause; 
     IF done THEN 
      -- No more rows so exit the loop. 
      LEAVE read_loop; 
     END IF; 

     SET @query = CONCAT(
      'DELETE FROM ', schema_name, '.', tbl_name, ' WHERE ', where_clause); 

     PREPARE stmt FROM @query; 
     EXECUTE stmt; 
     DEALLOCATE PREPARE stmt; 
     END LOOP; 
     CLOSE cursor2; 
    END IF; 

    -- Tidy up 
    DROP TEMPORARY TABLE IF EXISTS temp_deletes; 
    END IF; 

    -- Decrement current recursion depth since the stored procedure is being exited. 
    SET @recursion_depth = @recursion_depth - 1; 
END;// 
DELIMITER ; 

제한

  1. CREATE TEMPORARY TABLES 사용중인 스키마에 대해 저장 프로 시저를 실행하는 사용자에게 사용 권한이 필요합니다.
  2. MySQL은 최대 재귀 깊이 255를 지원하기 때문에 매우 많은 수의 외래 키 링크가있는 경우이 방법이 실패하게됩니다 (가능성이 낮음).
  3. "순환"/ "순환"외래 키 참조 (예 : 테이블 A에는 테이블 B에 외래 키가 있고 테이블 B에는 테이블 A에 외래 키가 있음)는 현재 지원되지 않으며 무한 루프가 발생합니다.
  4. "라이브"시스템에서 사용하도록 설계되지 않았습니다. 데이터가 반복적으로 삭제되므로 나중에 하위 레코드와 상위 레코드를 삭제하는 사이에 더 많은 데이터가 추가되면 삭제가 실패 할 수 있습니다.
관련 문제