“On silme basamaklama” kısıtlamaları nasıl eklenir?


163

PostgreSQL 8'de, ON DELETE CASCADESaşağıdaki tabloda her iki yabancı anahtarı ikincisini bırakmadan eklemek mümkün müdür ?

# \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)

Referans verilen her iki tablo da aşağıdadır:

# \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)

Ve burada:

# \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)

Ve ayrıca önceki tabloya 2 dizin eklemenin mantıklı olup olmadığını merak ediyorum.

GÜNCELLEME: Teşekkür ederim ve ayrıca posta listesinde 1 ifadede ve dolayısıyla açıkça bir işlem başlatmadan yönetebileceğim konusunda tavsiye aldım:

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

1
Biraz OT, ancak referans sütunlarında (örneğin, pref_scores.gid) dizin oluşturmadığınızı fark ettim . Başvurulan tablodaki silmeler, bu tablolarda çok sayıda satır alırsanız bunlar olmadan uzun zaman alacaktır. Bazı veritabanları başvuru sütunlarında otomatik olarak bir dizin oluşturur; Değerli olmadığı bazı durumlar olduğu için PostgreSQL bunu size bırakıyor.
kgrittn

1
Teşekkür ederim! Aslında silme işleminin uzun sürdüğünü fark ettim, ama bunun nedeni olduğunu bilmiyordum
Alexander Farber

1
Yabancı anahtarlar üzerindeki dizinler işe yaramazsa hangi durumlar olur?
Alexander Farber

2
Bulgunuzu cevabıma dahil ettim. (Bu tek ifade aynı zamanda tek bir işlemdir.)
Mike Sherrill 'Cat Recall'

2
@AlexanderFarber: Ne zaman bir FK'nin referans sütun (ları) ndaki bir dizini atlamak isteyebilirsiniz? Yeterince iyi çalışacak tam eşleşme olmayan başka bir dizin olduğunda (örneğin, sık sık benzerlik aramaları için FK silinmesi için de uygun olacak bir trigram dizininiz olabilir). Silme işlemleri nadir olduğunda ve mesai saatleri dışında planlanabildiğinde. Bir tabloda referans değerinin sık sık güncellenmesi durumunda. Referans tablosu çok küçük ancak sıklıkla güncelleniyorsa. İstisnalar, PostgreSQL topluluğunun otomatik hale getirmek yerine onun üzerinde kontrol sahibi olmasını tercih ettiği kadar sık ​​görülür.
kgrittn

Yanıtlar:


218

on delete cascadeMevcut bir yabancı anahtar kısıtlamasına ekleyemeyeceğinizden eminim . Önce kısıtlamayı bırakmanız, ardından doğru sürümü eklemeniz gerekir. Standart SQL'de, bunu yapmanın en kolay yolunun

  • bir işlem başlatmak,
  • yabancı anahtarı bırak,
  • ile yabancı bir anahtar ekleyin on delete cascadeve son olarak
  • işlemi yap

Değiştirmek istediğiniz her yabancı anahtar için bu işlemi tekrarlayın.

Ancak PostgreSQL, tek bir SQL ifadesinde birden fazla kısıtlama cümlesi kullanmanıza izin veren standart dışı bir uzantıya sahiptir. Örneğin

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

Bırakmak istediğiniz yabancı anahtar kısıtlamasının adını bilmiyorsanız, ya pgAdminIII'de bakabilirsiniz (sadece tablo adını tıklayıp DDL'ye bakabilir veya "Sınırlamalar" ı görene kadar hiyerarşiyi genişletebilirsiniz), veya bilgi şemasını sorgulayabilirsiniz .

select *
from information_schema.key_column_usage
where position_in_unique_constraint is not null

Teşekkürler, ben de öyle düşündüm - ama DIŞ ANAHTARLAR ile ne yapmalı? Sadece kolayca bırakılıp okunabilen kısıtlamalar mı (NOT NULL'a benzer)?
Alexander Farber

2
@AlexanderFarber: Evet, kolayca bırakıp ekleyebileceğiniz kısıtlamalar olarak adlandırılırlar. Ama muhtemelen bunu bir işlem içinde yapmak istersiniz. Cevabımı daha ayrıntılı olarak güncelledim.
Mike Sherrill 'Cat Recall'

PgAdminIII'de ot aramak için +1. Size DROP CONSTRAINT ve ADD CONSTRAINT komutlarını bile verir, böylece bir sorgu penceresine kopyalayıp yapıştırabilir ve komutu istediğiniz şekilde düzenleyebilirsiniz.
Dave Pile

Sorguyu yazdıktan sonra, Postgres GUI'mi
danneu

Büyük tablolar için, bu NOT VALIDayrı bir işlemle ve doğrulamak mümkün mü? Bununla ilgili cevapsız bir sorum var.
TheCloudlessSky

11

@Mike Sherrill Cat Recall'ın cevabından yola çıkarak, benim için çalışan bu oldu:

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

5

Kullanımı:

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

İşlev:

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;

Unutmayın: bu işlev ilk yabancı anahtarın niteliklerini kopyalamaz . Yalnızca yabancı tablo adı / sütun adı alır, geçerli anahtarı bırakır ve yenisiyle değiştirir .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.