如何禁止自定义列表包含来自不同基本列表的元素
how to disallow custom lists to have elements from different base lists
我有以下型号:
BASE_LIST:在这个 table 中我们存储基本列表。基本列表可以是例如大陆的城市列表、商店中的汽车类型等
ELEMENT:在这个 table 中,我们存储基本列表的元素。例如城市和汽车类型在这里(布达佩斯、伦敦、巴黎、欧宝、宝马、奥迪)
CUSTOM_LIST:在这个 table 中,我们存储列表的自定义。定制意味着过滤。例如,可以有一个名为 'european cities' 的自定义列表,它是城市的一个子集。或作为汽车子集的昂贵汽车。自定义列表必须只有一个父列表 - 基本列表,并且只能包含来自该基本列表的元素。
目前的表示是这样的:
BASE_LIST和ELEMENT之间的关系是一对多关系(一个元素只能是一个基本列表的一部分,但一个基本列表可以有多个元素)。
BASE_LIST和CUSTOM_LIST之间的关系是一对多的关系,每个自定义列表必须只有一个“父列表”。
CUSTOM_LIST和ELEMENT之间的关系是多对多关系,因为
- 一个元素可以是多个自定义列表的一部分:例如,一辆汽车可能在一个名为“昂贵的汽车”的自定义列表中,也可能在另一个名为“不可靠的汽车”的自定义列表中。
- 自定义列表当然可以有多个元素,因此是多对多关系。
问题在于此结构允许使用来自不同基本列表的元素的自定义列表。
我们想禁止这样做。换句话说,包含来自“汽车”基本列表的元素的自定义列表是可以的,包含来自“城市”列表元素的自定义列表是可以的,但是混合了汽车和城市的自定义列表是不行的。
有没有办法用标准约束(没有存储过程等)来禁止这种混合列表?
我为此创建了一个fiddle:
http://sqlfiddle.com/#!4/40801/2
DDL:
CREATE TABLE BASE_LIST
(
ID NUMBER (18) NOT NULL ,
NAME VARCHAR2 (50) NOT NULL
);
ALTER TABLE BASE_LIST ADD CONSTRAINT BASE_LIST_PK PRIMARY KEY ( ID ) ;
CREATE TABLE ELEMENT
(
ID NUMBER (18) NOT NULL ,
NAME VARCHAR2 (50) NOT NULL,
BASE_LIST_ID NUMBER (18) NOT NULL
);
ALTER TABLE ELEMENT ADD CONSTRAINT ELEMENT_PK PRIMARY KEY ( ID ) ;
ALTER TABLE ELEMENT ADD CONSTRAINT ELEMENT_FK_TO_BASE_LIST FOREIGN KEY ( BASE_LIST_ID ) REFERENCES BASE_LIST ( ID );
CREATE TABLE CUSTOM_LIST
(
ID NUMBER (18) NOT NULL ,
NAME VARCHAR2 (50) NOT NULL ,
BASE_LIST_ID NUMBER (18) NOT NULL
);
ALTER TABLE CUSTOM_LIST ADD CONSTRAINT CUSTOM_LIST_PK PRIMARY KEY ( ID ) ;
ALTER TABLE CUSTOM_LIST ADD CONSTRAINT CUSTOM_LIST_FK_TO_BASE_LIST FOREIGN KEY ( BASE_LIST_ID ) REFERENCES BASE_LIST ( ID );
CREATE TABLE CUSTOM_LISTS_ELEMENTS
(
CUSTOM_LIST_ID NUMBER (18) NOT NULL,
ELEMENT_ID NUMBER (18) NOT NULL
);
ALTER TABLE CUSTOM_LISTS_ELEMENTS ADD CONSTRAINT CUSTOM_LISTS_ELEMENTS_PK PRIMARY KEY ( CUSTOM_LIST_ID, ELEMENT_ID ) ;
ALTER TABLE CUSTOM_LISTS_ELEMENTS ADD CONSTRAINT FK_TO_CUSTOM_LIST FOREIGN KEY ( CUSTOM_LIST_ID ) REFERENCES CUSTOM_LIST ( ID );
ALTER TABLE CUSTOM_LISTS_ELEMENTS ADD CONSTRAINT FK_TO_ELEMENT FOREIGN KEY ( ELEMENT_ID ) REFERENCES ELEMENT ( ID );
问题:
insert into BASE_LIST values (1, 'cities');
insert into ELEMENT values (1, 'Budapest', 1);
insert into ELEMENT values (2, 'London', 1);
insert into ELEMENT values (3, 'Paris', 1);
insert into BASE_LIST values (2, 'cars');
insert into ELEMENT values (4, 'Opel', 2);
insert into ELEMENT values (5, 'Bmw', 2);
insert into ELEMENT values (6, 'Audi', 2);
insert into CUSTOM_LIST values (1, 'EuCities', 1);
insert into CUSTOM_LIST values (2, 'PriceyCars', 2);
-- the below two inserts are allowed, custom list 1 will have
-- only two elements from base list 1: 1 and 3
insert into CUSTOM_LISTS_ELEMENTS values (1, 1);
insert into CUSTOM_LISTS_ELEMENTS values (1, 3);
-- this should be forbidden, because element 4 is in base list 2,
-- but custom list 1 is only for elements from base list 1.
insert into CUSTOM_LISTS_ELEMENTS values (1, 4);
为了防止来自不同列表的自定义元素,您需要使用复合键。这些将沿着两个关系分支传播,并将强制每个自定义元素属于一个基本列表。
例如你可以这样做:
create table base_list (
id int primary key not null,
name varchar(50)
);
create table element (
id int not null,
name varchar(50),
base_list_id int references base_list (id),
primary key (base_list_id, id)
);
create table custom_list (
id int not null,
name varchar(50),
base_list_id int references base_list (id),
primary key (base_list_id, id)
);
create table_custom_list_element (
custom_list_id int not null,
base_list_id int not null,
element_id int not null,
constraint fk_clist foreign key (base_list_id, custom_list_id)
references custom_list (base_list_id, id),
constraint fk_celement foreign key (base_list_id, element_id)
references element (base_list_id, id)
);
特别注意最后 table 中的两个外键共享同一列 base_list_id
。这会强制执行您想要的规则。
解决方案应归功于“The Impaler”。
记录下来,这是使用复合外键的解决方案:
修改后的DDL:
CREATE TABLE BASE_LIST
(
ID NUMBER (18) NOT NULL ,
NAME VARCHAR2 (50) NOT NULL
);
ALTER TABLE BASE_LIST ADD CONSTRAINT BASE_LIST_PK PRIMARY KEY ( ID ) ;
CREATE TABLE ELEMENT
(
ID NUMBER (18) NOT NULL ,
NAME VARCHAR2 (50) NOT NULL,
BASE_LIST_ID NUMBER (18) NOT NULL
);
ALTER TABLE ELEMENT ADD CONSTRAINT ELEMENT_PK PRIMARY KEY ( ID ) ;
ALTER TABLE ELEMENT ADD CONSTRAINT ELEMENT_UK_ID_BASE_LIST_ID UNIQUE (ID, BASE_LIST_ID) ;
ALTER TABLE ELEMENT ADD CONSTRAINT ELEMENT_FK_TO_BASE_LIST FOREIGN KEY ( BASE_LIST_ID ) REFERENCES BASE_LIST ( ID );
CREATE TABLE CUSTOM_LIST
(
ID NUMBER (18) NOT NULL ,
NAME VARCHAR2 (50) NOT NULL ,
BASE_LIST_ID NUMBER (18) NOT NULL
);
ALTER TABLE CUSTOM_LIST ADD CONSTRAINT CUSTOM_LIST_PK PRIMARY KEY ( ID ) ;
ALTER TABLE CUSTOM_LIST ADD CONSTRAINT CUSTOM_LIST_UK_ID_BASE_LIST_ID UNIQUE (ID, BASE_LIST_ID) ;
ALTER TABLE CUSTOM_LIST ADD CONSTRAINT CUSTOM_LIST_FK_TO_BASE_LIST FOREIGN KEY ( BASE_LIST_ID ) REFERENCES BASE_LIST ( ID );
CREATE TABLE CUSTOM_LISTS_ELEMENTS
(
BASE_LIST_ID NUMBER (18) NOT NULL,
CUSTOM_LIST_ID NUMBER (18) NOT NULL,
ELEMENT_ID NUMBER (18) NOT NULL
);
ALTER TABLE CUSTOM_LISTS_ELEMENTS ADD CONSTRAINT CUSTOM_LISTS_ELEMENTS_PK PRIMARY KEY ( CUSTOM_LIST_ID, ELEMENT_ID ) ;
ALTER TABLE CUSTOM_LISTS_ELEMENTS ADD CONSTRAINT FK_TO_CUSTOM_LIST FOREIGN KEY ( CUSTOM_LIST_ID, BASE_LIST_ID ) REFERENCES CUSTOM_LIST ( ID, BASE_LIST_ID );
ALTER TABLE CUSTOM_LISTS_ELEMENTS ADD CONSTRAINT FK_TO_ELEMENT FOREIGN KEY ( ELEMENT_ID, BASE_LIST_ID ) REFERENCES ELEMENT ( ID, BASE_LIST_ID );
修改后的测试:
insert into BASE_LIST values (1, 'cities');
insert into ELEMENT values (1, 'Budapest', 1);
insert into ELEMENT values (2, 'London', 1);
insert into ELEMENT values (3, 'Paris', 1);
insert into BASE_LIST values (2, 'cars');
insert into ELEMENT values (4, 'Opel', 2);
insert into ELEMENT values (5, 'Bmw', 2);
insert into ELEMENT values (6, 'Audi', 2);
insert into CUSTOM_LIST values (1, 'EuCities', 1);
insert into CUSTOM_LIST values (2, 'PriceyCars', 2);
-- the below two inserts are allowed, custom list 1 will have
-- only two elements from base list 1: 1 and 3
insert into CUSTOM_LISTS_ELEMENTS values (1, 1, 1);
insert into CUSTOM_LISTS_ELEMENTS values (1, 1, 3);
-- this should be forbidden, because element 4 is in base list 2,
-- but custom list 1 is only for elements from base list 1.
insert into CUSTOM_LISTS_ELEMENTS values (2, 1, 4);
sql fiddle:
我有以下型号:
BASE_LIST:在这个 table 中我们存储基本列表。基本列表可以是例如大陆的城市列表、商店中的汽车类型等
ELEMENT:在这个 table 中,我们存储基本列表的元素。例如城市和汽车类型在这里(布达佩斯、伦敦、巴黎、欧宝、宝马、奥迪)
CUSTOM_LIST:在这个 table 中,我们存储列表的自定义。定制意味着过滤。例如,可以有一个名为 'european cities' 的自定义列表,它是城市的一个子集。或作为汽车子集的昂贵汽车。自定义列表必须只有一个父列表 - 基本列表,并且只能包含来自该基本列表的元素。
目前的表示是这样的:
BASE_LIST和ELEMENT之间的关系是一对多关系(一个元素只能是一个基本列表的一部分,但一个基本列表可以有多个元素)。
BASE_LIST和CUSTOM_LIST之间的关系是一对多的关系,每个自定义列表必须只有一个“父列表”。
CUSTOM_LIST和ELEMENT之间的关系是多对多关系,因为
- 一个元素可以是多个自定义列表的一部分:例如,一辆汽车可能在一个名为“昂贵的汽车”的自定义列表中,也可能在另一个名为“不可靠的汽车”的自定义列表中。
- 自定义列表当然可以有多个元素,因此是多对多关系。
问题在于此结构允许使用来自不同基本列表的元素的自定义列表。
我们想禁止这样做。换句话说,包含来自“汽车”基本列表的元素的自定义列表是可以的,包含来自“城市”列表元素的自定义列表是可以的,但是混合了汽车和城市的自定义列表是不行的。
有没有办法用标准约束(没有存储过程等)来禁止这种混合列表?
我为此创建了一个fiddle:
http://sqlfiddle.com/#!4/40801/2
DDL:
CREATE TABLE BASE_LIST
(
ID NUMBER (18) NOT NULL ,
NAME VARCHAR2 (50) NOT NULL
);
ALTER TABLE BASE_LIST ADD CONSTRAINT BASE_LIST_PK PRIMARY KEY ( ID ) ;
CREATE TABLE ELEMENT
(
ID NUMBER (18) NOT NULL ,
NAME VARCHAR2 (50) NOT NULL,
BASE_LIST_ID NUMBER (18) NOT NULL
);
ALTER TABLE ELEMENT ADD CONSTRAINT ELEMENT_PK PRIMARY KEY ( ID ) ;
ALTER TABLE ELEMENT ADD CONSTRAINT ELEMENT_FK_TO_BASE_LIST FOREIGN KEY ( BASE_LIST_ID ) REFERENCES BASE_LIST ( ID );
CREATE TABLE CUSTOM_LIST
(
ID NUMBER (18) NOT NULL ,
NAME VARCHAR2 (50) NOT NULL ,
BASE_LIST_ID NUMBER (18) NOT NULL
);
ALTER TABLE CUSTOM_LIST ADD CONSTRAINT CUSTOM_LIST_PK PRIMARY KEY ( ID ) ;
ALTER TABLE CUSTOM_LIST ADD CONSTRAINT CUSTOM_LIST_FK_TO_BASE_LIST FOREIGN KEY ( BASE_LIST_ID ) REFERENCES BASE_LIST ( ID );
CREATE TABLE CUSTOM_LISTS_ELEMENTS
(
CUSTOM_LIST_ID NUMBER (18) NOT NULL,
ELEMENT_ID NUMBER (18) NOT NULL
);
ALTER TABLE CUSTOM_LISTS_ELEMENTS ADD CONSTRAINT CUSTOM_LISTS_ELEMENTS_PK PRIMARY KEY ( CUSTOM_LIST_ID, ELEMENT_ID ) ;
ALTER TABLE CUSTOM_LISTS_ELEMENTS ADD CONSTRAINT FK_TO_CUSTOM_LIST FOREIGN KEY ( CUSTOM_LIST_ID ) REFERENCES CUSTOM_LIST ( ID );
ALTER TABLE CUSTOM_LISTS_ELEMENTS ADD CONSTRAINT FK_TO_ELEMENT FOREIGN KEY ( ELEMENT_ID ) REFERENCES ELEMENT ( ID );
问题:
insert into BASE_LIST values (1, 'cities');
insert into ELEMENT values (1, 'Budapest', 1);
insert into ELEMENT values (2, 'London', 1);
insert into ELEMENT values (3, 'Paris', 1);
insert into BASE_LIST values (2, 'cars');
insert into ELEMENT values (4, 'Opel', 2);
insert into ELEMENT values (5, 'Bmw', 2);
insert into ELEMENT values (6, 'Audi', 2);
insert into CUSTOM_LIST values (1, 'EuCities', 1);
insert into CUSTOM_LIST values (2, 'PriceyCars', 2);
-- the below two inserts are allowed, custom list 1 will have
-- only two elements from base list 1: 1 and 3
insert into CUSTOM_LISTS_ELEMENTS values (1, 1);
insert into CUSTOM_LISTS_ELEMENTS values (1, 3);
-- this should be forbidden, because element 4 is in base list 2,
-- but custom list 1 is only for elements from base list 1.
insert into CUSTOM_LISTS_ELEMENTS values (1, 4);
为了防止来自不同列表的自定义元素,您需要使用复合键。这些将沿着两个关系分支传播,并将强制每个自定义元素属于一个基本列表。
例如你可以这样做:
create table base_list (
id int primary key not null,
name varchar(50)
);
create table element (
id int not null,
name varchar(50),
base_list_id int references base_list (id),
primary key (base_list_id, id)
);
create table custom_list (
id int not null,
name varchar(50),
base_list_id int references base_list (id),
primary key (base_list_id, id)
);
create table_custom_list_element (
custom_list_id int not null,
base_list_id int not null,
element_id int not null,
constraint fk_clist foreign key (base_list_id, custom_list_id)
references custom_list (base_list_id, id),
constraint fk_celement foreign key (base_list_id, element_id)
references element (base_list_id, id)
);
特别注意最后 table 中的两个外键共享同一列 base_list_id
。这会强制执行您想要的规则。
解决方案应归功于“The Impaler”。
记录下来,这是使用复合外键的解决方案:
修改后的DDL:
CREATE TABLE BASE_LIST
(
ID NUMBER (18) NOT NULL ,
NAME VARCHAR2 (50) NOT NULL
);
ALTER TABLE BASE_LIST ADD CONSTRAINT BASE_LIST_PK PRIMARY KEY ( ID ) ;
CREATE TABLE ELEMENT
(
ID NUMBER (18) NOT NULL ,
NAME VARCHAR2 (50) NOT NULL,
BASE_LIST_ID NUMBER (18) NOT NULL
);
ALTER TABLE ELEMENT ADD CONSTRAINT ELEMENT_PK PRIMARY KEY ( ID ) ;
ALTER TABLE ELEMENT ADD CONSTRAINT ELEMENT_UK_ID_BASE_LIST_ID UNIQUE (ID, BASE_LIST_ID) ;
ALTER TABLE ELEMENT ADD CONSTRAINT ELEMENT_FK_TO_BASE_LIST FOREIGN KEY ( BASE_LIST_ID ) REFERENCES BASE_LIST ( ID );
CREATE TABLE CUSTOM_LIST
(
ID NUMBER (18) NOT NULL ,
NAME VARCHAR2 (50) NOT NULL ,
BASE_LIST_ID NUMBER (18) NOT NULL
);
ALTER TABLE CUSTOM_LIST ADD CONSTRAINT CUSTOM_LIST_PK PRIMARY KEY ( ID ) ;
ALTER TABLE CUSTOM_LIST ADD CONSTRAINT CUSTOM_LIST_UK_ID_BASE_LIST_ID UNIQUE (ID, BASE_LIST_ID) ;
ALTER TABLE CUSTOM_LIST ADD CONSTRAINT CUSTOM_LIST_FK_TO_BASE_LIST FOREIGN KEY ( BASE_LIST_ID ) REFERENCES BASE_LIST ( ID );
CREATE TABLE CUSTOM_LISTS_ELEMENTS
(
BASE_LIST_ID NUMBER (18) NOT NULL,
CUSTOM_LIST_ID NUMBER (18) NOT NULL,
ELEMENT_ID NUMBER (18) NOT NULL
);
ALTER TABLE CUSTOM_LISTS_ELEMENTS ADD CONSTRAINT CUSTOM_LISTS_ELEMENTS_PK PRIMARY KEY ( CUSTOM_LIST_ID, ELEMENT_ID ) ;
ALTER TABLE CUSTOM_LISTS_ELEMENTS ADD CONSTRAINT FK_TO_CUSTOM_LIST FOREIGN KEY ( CUSTOM_LIST_ID, BASE_LIST_ID ) REFERENCES CUSTOM_LIST ( ID, BASE_LIST_ID );
ALTER TABLE CUSTOM_LISTS_ELEMENTS ADD CONSTRAINT FK_TO_ELEMENT FOREIGN KEY ( ELEMENT_ID, BASE_LIST_ID ) REFERENCES ELEMENT ( ID, BASE_LIST_ID );
修改后的测试:
insert into BASE_LIST values (1, 'cities');
insert into ELEMENT values (1, 'Budapest', 1);
insert into ELEMENT values (2, 'London', 1);
insert into ELEMENT values (3, 'Paris', 1);
insert into BASE_LIST values (2, 'cars');
insert into ELEMENT values (4, 'Opel', 2);
insert into ELEMENT values (5, 'Bmw', 2);
insert into ELEMENT values (6, 'Audi', 2);
insert into CUSTOM_LIST values (1, 'EuCities', 1);
insert into CUSTOM_LIST values (2, 'PriceyCars', 2);
-- the below two inserts are allowed, custom list 1 will have
-- only two elements from base list 1: 1 and 3
insert into CUSTOM_LISTS_ELEMENTS values (1, 1, 1);
insert into CUSTOM_LISTS_ELEMENTS values (1, 1, 3);
-- this should be forbidden, because element 4 is in base list 2,
-- but custom list 1 is only for elements from base list 1.
insert into CUSTOM_LISTS_ELEMENTS values (2, 1, 4);
sql fiddle: