1

给定一个外键约束的简单示例:

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 的机制?

4

1 回答 1

1

这是一个没有引用计数的示例:

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;
于 2013-02-27T12:43:08.927 回答