举一个外键约束的简单例子:
CREATE TABLE products (
product_no integer PRIMARY KEY,
name text,
price numeric
);
CREATE TABLE orders (
order_id integer PRIMARY KEY,
shipping_address text,
...
);
CREATE TABLE order_items (
product_no integer REFERENCES products ON DELETE CASCADE,
order_id integer REFERENCES orders ON DELETE CASCADE,
quantity integer,
PRIMARY KEY (product_no, order_id)
);
当从产品或订单中删除一行时,引用 order_items 中的行也将被删除。
但是 - 可以让数据库检测产品或订单中的孤立行(没有引用 order_item 的行)并删除它们吗?我知道我可以设置一个查询来轻松完成它,但在更大更复杂的模式中,这可能是很多查询。不知道有没有类似ON DELETE CASCADE的机制?
最佳答案
这是一个没有引用计数的例子:
DROP SCHEMA tmp CASCADE;
CREATE SCHEMA tmp ;
SET search_path=tmp;
CREATE TABLE one
( id SERIAL NOT NULL PRIMARY KEY
, name VARCHAR
);
CREATE TABLE two
( id SERIAL NOT NULL PRIMARY KEY
, name VARCHAR
);
CREATE TABLE onetwo
( one_id INTEGER REFERENCES one(id)
, two_id INTEGER REFERENCES two(id)
, PRIMARY KEY(one_id, two_id)
);
CREATE INDEX onetwo_rev ON onetwo (two_id,one_id);
-- Populate the tables.
INSERT INTO one(name) select 'One_name_' || gs::text FROM generate_series(1,5) gs ;
INSERT INTO two(name) select 'Two_name_' || gs::text FROM generate_series(1,5) gs ;
INSERT INTO onetwo (one_id, two_id)
SELECT o.id, t.id
FROM one o
JOIN two t ON 1=1
;
-- Remove some random associations between one&two,
-- and remove any unreferenced records from one and two
DELETE FROM onetwo WHERE random() < 0.7;
DELETE FROM one dd WHERE NOT EXISTS(SELECT * FROM onetwo ot WHERE ot.one_id=dd.id) ;
DELETE FROM two dd WHERE NOT EXISTS(SELECT * FROM onetwo ot WHERE ot.two_id=dd.id) ;
SELECT * FROM onetwo;
SELECT * FROM one;
SELECT * FROM two;
CREATE OR REPLACE FUNCTION check_the_deletes() RETURNS TRIGGER AS $meat$
BEGIN
DELETE FROM one dd
WHERE dd.id=OLD.one_id
AND NOT EXISTS(SELECT * FROM onetwo ot WHERE ot.one_id=dd.id)
;
DELETE FROM two dd
WHERE dd.id=OLD.two_id
AND NOT EXISTS(SELECT * FROM onetwo ot WHERE ot.two_id=dd.id)
;
RETURN NEW;
END; $meat$
LANGUAGE plpgsql;
CREATE TRIGGER update_last_sale
AFTER DELETE ON onetwo
FOR EACH ROW
EXECUTE PROCEDURE check_the_deletes()
;
SELECT o.name AS name1
, t.name AS name2
FROM onetwo ot
JOIN one o ON o.id = ot.one_id
JOIN two t ON t.id = ot.two_id
;
-- Delete some random associations
-- the trigger should also remove any unreferenced rows
-- from one and two tables.
DELETE FROM onetwo WHERE random() < 0.4;
SELECT o.name AS name1
, t.name AS name2
FROM onetwo ot
JOIN one o ON o.id = ot.one_id
JOIN two t ON t.id = ot.two_id
;
SELECT * FROM one;
SELECT * FROM two;
关于SQL 约束和 ON DELETE,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15087590/