Làm cách nào để thêm các dòng trên xóa các ràng buộc xếp tầng?


163

Trong PostgreSQL 8 có thể thêm ON DELETE CASCADESvào cả hai khóa ngoại trong bảng sau mà không bỏ cái sau không?

# \d scores
        Table "public.scores"
 Column  |         Type          | Modifiers
---------+-----------------------+-----------
 id      | character varying(32) |
 gid     | integer               |
 money   | integer               | not null
 quit    | boolean               |
 last_ip | inet                  |
Foreign-key constraints:
   "scores_gid_fkey" FOREIGN KEY (gid) REFERENCES games(gid)
   "scores_id_fkey" FOREIGN KEY (id) REFERENCES users(id)

Cả hai bảng được tham chiếu bên dưới - ở đây:

# \d games
                                     Table "public.games"
  Column  |            Type             |                        Modifiers
----------+-----------------------------+----------------------------------------------------------
 gid      | integer                     | not null default nextval('games_gid_seq'::regclass)
 rounds   | integer                     | not null
 finished | timestamp without time zone | default now()
Indexes:
    "games_pkey" PRIMARY KEY, btree (gid)
Referenced by:
    TABLE "scores" CONSTRAINT "scores_gid_fkey" FOREIGN KEY (gid) REFERENCES games(gid)

Và đây:

# \d users
                Table "public.users"
   Column   |            Type             |   Modifiers
------------+-----------------------------+---------------
 id         | character varying(32)       | not null
 first_name | character varying(64)       |
 last_name  | character varying(64)       |
 female     | boolean                     |
 avatar     | character varying(128)      |
 city       | character varying(64)       |
 login      | timestamp without time zone | default now()
 last_ip    | inet                        |
 logout     | timestamp without time zone |
 vip        | timestamp without time zone |
 mail       | character varying(254)      |
Indexes:
    "users_pkey" PRIMARY KEY, btree (id)
Referenced by:
    TABLE "cards" CONSTRAINT "cards_id_fkey" FOREIGN KEY (id) REFERENCES users(id)
    TABLE "catch" CONSTRAINT "catch_id_fkey" FOREIGN KEY (id) REFERENCES users(id)
    TABLE "chat" CONSTRAINT "chat_id_fkey" FOREIGN KEY (id) REFERENCES users(id)
    TABLE "game" CONSTRAINT "game_id_fkey" FOREIGN KEY (id) REFERENCES users(id)
    TABLE "hand" CONSTRAINT "hand_id_fkey" FOREIGN KEY (id) REFERENCES users(id)
    TABLE "luck" CONSTRAINT "luck_id_fkey" FOREIGN KEY (id) REFERENCES users(id)
    TABLE "match" CONSTRAINT "match_id_fkey" FOREIGN KEY (id) REFERENCES users(id)
    TABLE "misere" CONSTRAINT "misere_id_fkey" FOREIGN KEY (id) REFERENCES users(id)
    TABLE "money" CONSTRAINT "money_id_fkey" FOREIGN KEY (id) REFERENCES users(id)
    TABLE "pass" CONSTRAINT "pass_id_fkey" FOREIGN KEY (id) REFERENCES users(id)
    TABLE "payment" CONSTRAINT "payment_id_fkey" FOREIGN KEY (id) REFERENCES users(id)
    TABLE "rep" CONSTRAINT "rep_author_fkey" FOREIGN KEY (author) REFERENCES users(id)
    TABLE "rep" CONSTRAINT "rep_id_fkey" FOREIGN KEY (id) REFERENCES users(id)
    TABLE "scores" CONSTRAINT "scores_id_fkey" FOREIGN KEY (id) REFERENCES users(id)
    TABLE "status" CONSTRAINT "status_id_fkey" FOREIGN KEY (id) REFERENCES users(id)

Và tôi cũng tự hỏi liệu có hợp lý khi thêm 2 chỉ mục vào bảng cũ không?

CẬP NHẬT: Cảm ơn bạn, và tôi cũng đã nhận được lời khuyên trong danh sách gửi thư, rằng tôi có thể quản lý nó trong 1 tuyên bố và do đó mà không bắt đầu giao dịch một cách rõ ràng:

ALTER TABLE public.scores
DROP CONSTRAINT scores_gid_fkey,
ADD CONSTRAINT scores_gid_fkey
   FOREIGN KEY (gid)
   REFERENCES games(gid)
   ON DELETE CASCADE;

1
Một chút OT, nhưng tôi nhận thấy rằng bạn chưa tạo chỉ mục trên các cột tham chiếu (ví dụ pref_scores.gid:). Xóa trên bảng được tham chiếu sẽ mất nhiều thời gian mà không có chúng, nếu bạn nhận được nhiều hàng trong các bảng đó. Một số cơ sở dữ liệu tự động tạo một chỉ mục trên (các) cột tham chiếu; PostgreSQL tùy thuộc vào bạn, vì có một số trường hợp không đáng.
kgrittn

1
Cảm ơn bạn! Tôi thực sự nhận thấy rằng việc xóa sẽ mất nhiều thời gian, nhưng không biết đó là lý do
Alexander Farber

1
Những trường hợp nào sẽ xảy ra, khi các chỉ mục trên khóa ngoại không đáng giá?
Alexander Farber

2
Tôi kết hợp phát hiện của bạn vào câu trả lời của tôi. (Tuyên bố duy nhất đó cũng là một giao dịch duy nhất.)
Mike Sherrill 'Cat Recall'

2
@AlexanderFarber: Khi nào bạn có thể muốn bỏ qua một chỉ mục trên (các) cột tham chiếu của FK? Khi có một chỉ mục khác không phải là một kết hợp chính xác, nó sẽ hoạt động đủ tốt (ví dụ: bạn có thể có một chỉ số trigram cho các tìm kiếm tương tự thường xuyên cũng sẽ ổn khi xóa FK). Khi xóa không thường xuyên và có thể được lên lịch ngoài giờ. Khi một bảng có cập nhật thường xuyên của giá trị tham chiếu. Khi bảng tham chiếu rất nhỏ nhưng được cập nhật thường xuyên. Các ngoại lệ xảy ra thường xuyên đủ để cộng đồng PostgreQuery thích kiểm soát nó hơn là làm cho nó tự động.
kgrittn

Câu trả lời:


218

Tôi khá chắc chắn rằng bạn không thể đơn giản thêm on delete cascadevào một ràng buộc khóa ngoại hiện có. Bạn phải loại bỏ các ràng buộc trước, sau đó thêm phiên bản chính xác. Trong SQL chuẩn, tôi tin rằng cách dễ nhất để làm điều này là

  • bắt đầu một giao dịch
  • đánh rơi khóa ngoại
  • thêm khóa ngoại với on delete cascadevà cuối cùng
  • cam kết giao dịch

Lặp lại cho mỗi khóa ngoại bạn muốn thay đổi.

Nhưng PostgreSQL có một phần mở rộng không chuẩn cho phép bạn sử dụng nhiều mệnh đề ràng buộc trong một câu lệnh SQL. Ví dụ

alter table public.scores
drop constraint scores_gid_fkey,
add constraint scores_gid_fkey
   foreign key (gid)
   references games(gid)
   on delete cascade;

Nếu bạn không biết tên của ràng buộc khóa ngoại bạn muốn loại bỏ, bạn có thể tra cứu nó trong pgAdminIII (chỉ cần nhấp vào tên bảng và xem DDL hoặc mở rộng phân cấp cho đến khi bạn thấy "Ràng buộc"), hoặc bạn có thể truy vấn lược đồ thông tin .

select *
from information_schema.key_column_usage
where position_in_unique_constraint is not null

Cảm ơn, đó là những gì tôi cũng nghĩ - nhưng phải làm gì với các phím NGOẠI TỆ? Có phải chúng chỉ là các ràng buộc (tương tự như KHÔNG NULL) có thể được loại bỏ và được gắn lại dễ dàng không?
Alexander Farber

2
@AlexanderFarber: Có, chúng được đặt tên là các ràng buộc mà bạn có thể bỏ và thêm dễ dàng. Nhưng bạn có thể muốn làm điều đó trong một giao dịch. Cập nhật câu trả lời của tôi với nhiều chi tiết hơn.
Mike Sherrill 'Nhớ lại mèo'

+1 để tìm kiếm trong pgAdminIII. Nó thậm chí còn cung cấp cho bạn các lệnh DROP CONSTRAINT và ADD CONSTRAINT, vì vậy bạn chỉ cần sao chép và dán vào cửa sổ truy vấn và chỉnh sửa lệnh theo ý muốn.
Dave Pile

Sau khi viết truy vấn, tôi nhận thấy GUI Postgres của mình (Navicat) hãy để tôi thực hiện thay đổi này một cách tầm thường từ trong GUI: dl.dropboxusercontent.com/spa/quq37nq1583x0lf/wwqne-lw.png
danneu

Đối với các bảng lớn, điều này có thể với NOT VALIDvà xác nhận trong một giao dịch riêng biệt không? Tôi có một câu hỏi chưa được trả lời về điều này.
TheCloudlessSky

11

Dựa trên câu trả lời của @Mike Sherrill Cat Recall, đây là những gì làm việc cho tôi:

ALTER TABLE "Children"
DROP CONSTRAINT "Children_parentId_fkey",
ADD CONSTRAINT "Children_parentId_fkey"
  FOREIGN KEY ("parentId")
  REFERENCES "Parent"(id)
  ON DELETE CASCADE;

5

Sử dụng:

select replace_foreign_key('user_rates_posts', 'post_id', 'ON DELETE CASCADE');

Chức năng:

CREATE OR REPLACE FUNCTION 
    replace_foreign_key(f_table VARCHAR, f_column VARCHAR, new_options VARCHAR) 
RETURNS VARCHAR
AS $$
DECLARE constraint_name varchar;
DECLARE reftable varchar;
DECLARE refcolumn varchar;
BEGIN

SELECT tc.constraint_name, ccu.table_name AS foreign_table_name, ccu.column_name AS foreign_column_name 
FROM 
    information_schema.table_constraints AS tc 
    JOIN information_schema.key_column_usage AS kcu
      ON tc.constraint_name = kcu.constraint_name
    JOIN information_schema.constraint_column_usage AS ccu
      ON ccu.constraint_name = tc.constraint_name
WHERE constraint_type = 'FOREIGN KEY' 
   AND tc.table_name= f_table AND kcu.column_name= f_column
INTO constraint_name, reftable, refcolumn;

EXECUTE 'alter table ' || f_table || ' drop constraint ' || constraint_name || 
', ADD CONSTRAINT ' || constraint_name || ' FOREIGN KEY (' || f_column || ') ' ||
' REFERENCES ' || reftable || '(' || refcolumn || ') ' || new_options || ';';

RETURN 'Constraint replaced: ' || constraint_name || ' (' || f_table || '.' || f_column ||
 ' -> ' || reftable || '.' || refcolumn || '); New options: ' || new_options;

END;
$$ LANGUAGE plpgsql;

Lưu ý: chức năng này sẽ không sao chép các thuộc tính của khóa ngoại ban đầu. Nó chỉ lấy tên bảng / tên cột nước ngoài, bỏ khóa hiện tại và thay thế bằng cái mới.

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.