两个递归多对多关系之间的约束
Constraining between two recursive many-to-many relationships
我有以下 table 用于创建任意数量的不同类型的项目。
CREATE TABLE item_types (
id SERIAL,
PRIMARY KEY (id)
-- Other columns omitted
);
CREATE TABLE items (
id SERIAL,
itemtype integer NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY (itemtype) REFERENCES item_types (id)
-- Other columns omitted
);
items
table 有一个称为 item_relationship
的递归多对多关系。
CREATE TABLE item_relationships (
itemid1 integer,
itemid2 integer,
PRIMARY KEY (itemid1, itemid2),
FOREIGN KEY (itemid1) REFERENCES items (id),
FOREIGN KEY (itemid2) REFERENCES items (id)
);
item_types
table 有一个称为 item_relationship_types
的递归多对多关系。
CREATE TABLE item_relationship_types (
type1 integer,
type2 integer,
PRIMARY KEY (type1, type2),
FOREIGN KEY (type1) REFERENCES item_types (id),
FOREIGN KEY (type2) REFERENCES item_types (id)
);
现在,我想做的是以某种方式有一个约束,你不能不小心创建一个无效的 item_relationship
,即在其中找不到项目的 item_types
任何 item_relationship_type
。我有两个问题。
这样的约束有意义吗?我认为插入错误的关系是业务逻辑中很容易发生的错误,因此在数据库中进行约束感觉很重要。
实际实施约束的明智方法是什么?
- 这并不完美,但似乎有效
CREATE FUNCTION item_check_types( ) RETURNS TRIGGER AS
$func$
BEGIN
IF EXISTS (
SELECT 1
FROM item_relationship_types irt
JOIN items it1 ON it1.itemtype = irt.type1
JOIN items it2 ON it2.itemtype = irt.type2
WHERE (it1.id = NEW.itemid1 AND it2.id = NEW.itemid2)
-- OR (it1.id = NEW.itemid2 AND it2.id = NEW.itemid1)
) THEN RETURN NEW;
ELSE
RAISE EXCEPTION 'type lookup failure';
RETURN NULL;
END IF;
END;
$func$ LANGUAGE 'plpgsql'
;
CREATE CONSTRAINT TRIGGER item_check_types
AFTER UPDATE OR INSERT
-- BEFORE UPDATE OR INSERT
ON item_relationships
FOR EACH ROW
EXECUTE PROCEDURE item_check_types()
;
INSERT INTO item_types(id)
SELECT generate_series(1,10);
INSERT INTO item_relationship_types (type1, type2) VALUES
(1,3), (2,4), (3,5), (4,6);
INSERT INTO items(id, itemtype)
SELECT gs, gs % 10
FROM generate_series(101,109) gs;
INSERT INTO item_relationships(itemid1, itemid2)
VALUES (101,103), (102,104); -- Okay
INSERT INTO item_relationships(itemid1, itemid2)
VALUES (101,104), (102,103); -- should fail
一种可能的方法是使用代理主键扩展 item_relationship_types
table:
CREATE TABLE item_relationship_types (
id integer SERIAL,
type1 integer,
type2 integer,
PRIMARY KEY (id),
UNIQUE (type1, type2),
FOREIGN KEY (type1) REFERENCES item_types (id),
FOREIGN KEY (type2) REFERENCES item_types (id)
);
然后将指向该代理键的外键添加到 item_relationships
table:
CREATE TABLE item_relationships (
itemid1 integer,
itemid2 integer,
type_rel_id integer not null,
PRIMARY KEY (itemid1, itemid2),
FOREIGN KEY (itemid1) REFERENCES items (id),
FOREIGN KEY (itemid2) REFERENCES items (id),
FOREIGN KEY (type_rel_id) REFERENCES item_relationship_types (id)
);
您还需要创建一个触发器,以防止在 item_relationships
table 中输入 type_rel_id
值,该值指向与 [=12= 中的这两项无关的类型的条目] table.
我有以下 table 用于创建任意数量的不同类型的项目。
CREATE TABLE item_types (
id SERIAL,
PRIMARY KEY (id)
-- Other columns omitted
);
CREATE TABLE items (
id SERIAL,
itemtype integer NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY (itemtype) REFERENCES item_types (id)
-- Other columns omitted
);
items
table 有一个称为 item_relationship
的递归多对多关系。
CREATE TABLE item_relationships (
itemid1 integer,
itemid2 integer,
PRIMARY KEY (itemid1, itemid2),
FOREIGN KEY (itemid1) REFERENCES items (id),
FOREIGN KEY (itemid2) REFERENCES items (id)
);
item_types
table 有一个称为 item_relationship_types
的递归多对多关系。
CREATE TABLE item_relationship_types (
type1 integer,
type2 integer,
PRIMARY KEY (type1, type2),
FOREIGN KEY (type1) REFERENCES item_types (id),
FOREIGN KEY (type2) REFERENCES item_types (id)
);
现在,我想做的是以某种方式有一个约束,你不能不小心创建一个无效的 item_relationship
,即在其中找不到项目的 item_types
任何 item_relationship_type
。我有两个问题。
这样的约束有意义吗?我认为插入错误的关系是业务逻辑中很容易发生的错误,因此在数据库中进行约束感觉很重要。
实际实施约束的明智方法是什么?
- 这并不完美,但似乎有效
CREATE FUNCTION item_check_types( ) RETURNS TRIGGER AS
$func$
BEGIN
IF EXISTS (
SELECT 1
FROM item_relationship_types irt
JOIN items it1 ON it1.itemtype = irt.type1
JOIN items it2 ON it2.itemtype = irt.type2
WHERE (it1.id = NEW.itemid1 AND it2.id = NEW.itemid2)
-- OR (it1.id = NEW.itemid2 AND it2.id = NEW.itemid1)
) THEN RETURN NEW;
ELSE
RAISE EXCEPTION 'type lookup failure';
RETURN NULL;
END IF;
END;
$func$ LANGUAGE 'plpgsql'
;
CREATE CONSTRAINT TRIGGER item_check_types
AFTER UPDATE OR INSERT
-- BEFORE UPDATE OR INSERT
ON item_relationships
FOR EACH ROW
EXECUTE PROCEDURE item_check_types()
;
INSERT INTO item_types(id)
SELECT generate_series(1,10);
INSERT INTO item_relationship_types (type1, type2) VALUES
(1,3), (2,4), (3,5), (4,6);
INSERT INTO items(id, itemtype)
SELECT gs, gs % 10
FROM generate_series(101,109) gs;
INSERT INTO item_relationships(itemid1, itemid2)
VALUES (101,103), (102,104); -- Okay
INSERT INTO item_relationships(itemid1, itemid2)
VALUES (101,104), (102,103); -- should fail
一种可能的方法是使用代理主键扩展 item_relationship_types
table:
CREATE TABLE item_relationship_types (
id integer SERIAL,
type1 integer,
type2 integer,
PRIMARY KEY (id),
UNIQUE (type1, type2),
FOREIGN KEY (type1) REFERENCES item_types (id),
FOREIGN KEY (type2) REFERENCES item_types (id)
);
然后将指向该代理键的外键添加到 item_relationships
table:
CREATE TABLE item_relationships (
itemid1 integer,
itemid2 integer,
type_rel_id integer not null,
PRIMARY KEY (itemid1, itemid2),
FOREIGN KEY (itemid1) REFERENCES items (id),
FOREIGN KEY (itemid2) REFERENCES items (id),
FOREIGN KEY (type_rel_id) REFERENCES item_relationship_types (id)
);
您还需要创建一个触发器,以防止在 item_relationships
table 中输入 type_rel_id
值,该值指向与 [=12= 中的这两项无关的类型的条目] table.