MySQL - Xóa hàng có ràng buộc khóa ngoài tham chiếu đến chính nó


12

Tôi có một bảng trong đó tôi lưu trữ tất cả các tin nhắn diễn đàn được đăng bởi người dùng trên trang web của tôi. Cấu trúc phân cấp thông điệp được triển khai bằng mô hình tập hợp Nested .

Sau đây là cấu trúc đơn giản của bảng:

  • Id (KHÓA CHÍNH)
  • Chủ sở hữu_Id (TÀI LIỆU THAM KHẢO NGOẠI TỆ ĐẾN Id )
  • Parent_Id (TÀI LIỆU THAM KHẢO NGOẠI TỆ ĐẾN Id )
  • nleft
  • không sao
  • nlevel

Bây giờ, cái bàn trông giống như thế này:

+ ------- + ------------- + -------------- + ---------- + ----------- + ----------- +
| Id      | Owner_Id      | Parent_Id      | nleft      | nright      | nlevel      |
+ ------- + ------------- + -------------- + ---------- + ----------- + ----------- +
| 1       | 1             | NULL           | 1          | 8           | 1           |
| 2       | 1             | 1              | 2          | 5           | 2           |
| 3       | 1             | 2              | 3          | 4           | 3           |
| 4       | 1             | 1              | 6          | 7           | 2           |
+ ------- + ------------- + -------------- + ---------- + ----------- + ----------- +

Lưu ý rằng hàng đầu tiên là thông điệp gốc và cây của bài đăng này có thể được hiển thị dưới dạng:

-- SELECT * FROM forumTbl WHERE Owner_Id = 1 ORDER BY nleft;

MESSAGE (Id = 1)
    MESSAGE (Id = 2)
        Message (Id = 3)
    Message (Id = 4)

Vấn đề của tôi xảy ra khi tôi cố gắng xóa tất cả các hàng trong cùng Owner_Idmột truy vấn. Thí dụ:

DELETE FROM forumTbl WHERE Owner_Id = 1 ORDER BY nright;

Truy vấn trên không thành công với lỗi sau:

Mã lỗi: 1451. Không thể xóa hoặc cập nhật hàng cha mẹ: một ràng buộc khóa ngoại không thành công ( forumTbl, CONSTRAINT Owner_Id_frgnFOREIGN KEY ( Owner_Id) TÀI LIỆU THAM KHẢO forumTbl( Id) TRÊN XÓA KHÔNG CÓ HÀNH ĐỘNG CẬP NHẬT KHÔNG CÓ HÀNH ĐỘNG)

Lý do là hàng đầu tiên , là nút gốc ( Id=1), cũng có cùng giá trị trong Owner_Idtrường của nó ( Owner_Id=1) và nó khiến truy vấn không thành công do ràng buộc khóa ngoài.

Câu hỏi của tôi là: Làm thế nào tôi có thể ngăn chặn vòng tròn ràng buộc khóa ngoại này và xóa một hàng tham chiếu đến chính nó? Có cách nào để làm điều đó mà không cần phải cập nhật Owner_Idhàng gốc lên NULLkhông?

Tôi đã tạo một bản demo của kịch bản này: http://sqlfiddle.com/#!9/fd1b1

Cảm ơn bạn.

Câu trả lời:


9
  1. Bên cạnh việc vô hiệu hóa khóa ngoại là nguy hiểm và có thể dẫn đến sự không nhất quán, có hai lựa chọn khác để xem xét:

  2. Sửa đổi các FOREIGN KEYràng buộc với ON DELETE CASCADEtùy chọn. Tôi đã không kiểm tra tất cả các trường hợp nhưng bạn chắc chắn cần cái này cho (owner_id)khóa ngoại và cũng có thể cho cái kia.

    ALTER TABLE forum
        DROP FOREIGN KEY owner_id_frgn,
        DROP FOREIGN KEY parent_id_frgn ;
    ALTER TABLE forum
        ADD CONSTRAINT owner_id_frgn
            FOREIGN KEY (owner_id) 
            REFERENCES forum (id)
            ON DELETE CASCADE,
        ADD CONSTRAINT parent_id_frgn
            FOREIGN KEY (parent_id) 
            REFERENCES forum (id)
            ON DELETE CASCADE ;

    Nếu bạn làm điều này, sau đó xóa một nút và tất cả các hậu duệ từ cây sẽ đơn giản hơn. Bạn xóa một nút và tất cả các hậu duệ sẽ bị xóa thông qua các hành động xếp tầng:

    DELETE FROM forum
    WHERE id = 1 ;         -- deletes id=1 and all descendants
  3. Vấn đề mà bạn bước vào thực sự là 2 vấn đề. Đầu tiên là việc xóa khỏi bảng có khóa ngoại tự tham chiếu không phải là vấn đề nghiêm trọng đối với MySQL, miễn là không có hàng nào tham chiếu chính nó. Nếu có một hàng, như trong ví dụ của bạn, các tùy chọn bị giới hạn. Vô hiệu hóa khóa ngoại hoặc sử dụng CASCADEhành động. Nhưng nếu không có hàng nào như vậy, việc xóa sẽ trở thành một vấn đề nhỏ hơn.

    Vì vậy, nếu chúng tôi quyết định để lưu trữ NULLthay vì cùng idowner_id, sau đó bạn có thể xóa mà không cần vô hiệu hóa các phím nước ngoài và không có thác.

    Sau đó, bạn sẽ vấp ngã vấn đề thứ hai! Chạy truy vấn của bạn sẽ gây ra một lỗi tương tự:

    DELETE FROM forum 
    WHERE owner_id = 1 OR id = 1 ; 

    Lỗi (s), cảnh báo:
    Không thể xóa hoặc cập nhật hàng cha mẹ: một ràng buộc khóa ngoại không thành công (rextester.forum, CONSTRAINT own_id_frgn KEY FOREIGN KEY (own_Id) diễn đàn TÀI LIỆU THAM KHẢO (id)

    Lý do cho lỗi này sẽ khác so với trước đây. Đó là bởi vì MySQL kiểm tra từng ràng buộc sau mỗi hàng bị xóa và không (như nó phải) tại lần thứ hai của câu lệnh. Vì vậy, khi cha mẹ bị xóa trước khi con của nó bị xóa, chúng ta sẽ gặp lỗi ràng buộc khóa ngoại.

    May mắn thay, có một giải pháp đơn giản cho việc này, đó là mô hình tập hợp lồng nhau và với điều đó, MySQL cho phép chúng ta thiết lập một trật tự cho việc xóa. Chúng tôi chỉ phải đặt hàng theo nleft DESChoặc bởi nright DESC, đảm bảo rằng tất cả trẻ em sẽ bị xóa trước khi cha mẹ:

    DELETE FROM forum 
    WHERE owner_id = 1 OR id = 1 
    ORDER BY nleft DESC ; 

    Lưu ý nhỏ, chúng ta có thể (hoặc nên) sử dụng một điều kiện xem xét mô hình lồng nhau là tốt. Điều này là tương đương (và có thể sử dụng một chỉ mục trên (nleft, nright)để tìm các nút cần xóa:

    DELETE FROM forum 
    WHERE nleft >= 1 AND nright <= 8 
    ORDER BY nleft DESC ; 

5
SET FOREIGN_KEY_CHECKS=0;
DELETE FROM forum WHERE Owner_Id = 1 ORDER BY nright;
SET FOREIGN_KEY_CHECKS=1;

chỉ không quên trong trường hợp này Bạn phải phân tích thủ công các tình huống khi Parent_id hiển thị thành 1, vì Bạn không sử dụng tầng

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.