我可以使用同一个字段来引用两个 table,而这两个 table 最终都引用相同的 table 吗?
Can I use the same field to reference two tables, which both ultimately reference the same table?
我设计这个数据库是为了将树木添加到森林中,每个森林只允许一种树。我想确保通过“forest_has_tree”table 添加到森林中的所有树木都属于该森林的正确类型。
我建议的设计是在“forest_has_tree”table 中包含“tree_type_idtree_type”字段,它引用“森林”中的“tree_type_idtree_type”table 以及“树”中的“tree_type_idtree_type”table(通过“tree_sub_type”table)。
CONSTRAINT `fk_forest_has_tree_forest1`
FOREIGN KEY (`forest_idforest` , `tree_type_idtree_type`)
REFERENCES `forest` (`idforest` , `tree_type_idtree_type`),
CONSTRAINT `fk_forest_has_tree_tree1`
FOREIGN KEY (`tree_idtree` , `tree_tree_sub_type_idtree_sub_type` , `tree_type_idtree_type`)
REFERENCES `tree` (`idtree` , `tree_sub_type_idtree_sub_type` , `tree_sub_type_tree_type_idtree_type`),
谁能评论一下这是否是一个可接受的table设计,或者是否有更好的方法来解决这个问题?
我在下面包含了这五个 table 的完整 MySQL。
我绝对相信没有森林会有超过一种树种。为了争论起见,假设我的森林都是 运行 独裁统治,如果有人种植未经授权的树,他们将与树一起蒸发,而且最重要的是不会保留任何记录。
-- -----------------------------------------------------
-- Table `tree_type`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `tree_type` (
`idtree_type` INT NOT NULL,
PRIMARY KEY (`idtree_type`))
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `forest`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `forest` (
`idforest` INT NOT NULL,
`tree_type_idtree_type` INT NOT NULL,
PRIMARY KEY (`idforest`, `tree_type_idtree_type`),
INDEX `fk_forest_tree_type1_idx` (`tree_type_idtree_type` ASC) VISIBLE,
CONSTRAINT `fk_forest_tree_type1`
FOREIGN KEY (`tree_type_idtree_type`)
REFERENCES `tree_type` (`idtree_type`)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `tree_sub_type`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `tree_sub_type` (
`idtree_sub_type` INT NOT NULL,
`tree_type_idtree_type` INT NOT NULL,
PRIMARY KEY (`idtree_sub_type`, `tree_type_idtree_type`),
CONSTRAINT `fk_tree_sub_type_tree_type1`
FOREIGN KEY (`tree_type_idtree_type`)
REFERENCES `tree_type` (`idtree_type`)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `tree`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `tree` (
`idtree` INT NOT NULL,
`tree_sub_type_idtree_sub_type` INT NOT NULL,
`tree_sub_type_tree_type_idtree_type` INT NOT NULL,
PRIMARY KEY (`idtree`, `tree_sub_type_idtree_sub_type`, `tree_sub_type_tree_type_idtree_type`),
INDEX `fk_tree_tree_sub_type1_idx` (`tree_sub_type_idtree_sub_type` ASC, `tree_sub_type_tree_type_idtree_type` ASC) VISIBLE,
CONSTRAINT `fk_tree_tree_sub_type1`
FOREIGN KEY (`tree_sub_type_idtree_sub_type` , `tree_sub_type_tree_type_idtree_type`)
REFERENCES `tree_sub_type` (`idtree_sub_type` , `tree_type_idtree_type`)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `forest_has_tree`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `forest_has_tree` (
`forest_idforest` INT NOT NULL,
`tree_type_idtree_type` INT NOT NULL,
`tree_idtree` INT NOT NULL,
`tree_tree_sub_type_idtree_sub_type` INT NOT NULL,
PRIMARY KEY (`forest_idforest`, `tree_type_idtree_type`, `tree_idtree`, `tree_tree_sub_type_idtree_sub_type`),
INDEX `fk_forest_has_tree_tree1_idx` (`tree_idtree` ASC, `tree_tree_sub_type_idtree_sub_type` ASC, `tree_type_idtree_type` ASC) VISIBLE,
INDEX `fk_forest_has_tree_forest1_idx` (`forest_idforest` ASC, `tree_type_idtree_type` ASC) VISIBLE,
CONSTRAINT `fk_forest_has_tree_forest1`
FOREIGN KEY (`forest_idforest` , `tree_type_idtree_type`)
REFERENCES `forest` (`idforest` , `tree_type_idtree_type`)
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `fk_forest_has_tree_tree1`
FOREIGN KEY (`tree_idtree` , `tree_tree_sub_type_idtree_sub_type` , `tree_type_idtree_type`)
REFERENCES `tree` (`idtree` , `tree_sub_type_idtree_sub_type` , `tree_sub_type_tree_type_idtree_type`)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
在我看来,您的架构只需要以下内容(为简洁起见省略了 FK 约束和代理 ID)...
编辑以添加一个工作示例...
DROP TABLE IF EXISTS trees;
CREATE TABLE trees
(tree_group VARCHAR(12) NOT NULL
,species VARCHAR(50) NOT NULL PRIMARY KEY
);
INSERT INTO trees VALUES
('Alder','Alnus glutinosa'),
('Ash','Fraxinus excelsior'),
('Birch','Downy Birch'),
('Birch','Silver Birch'),
('Birch','Betula pendula'),
('Cherry','Bird Cherry'),
('Cherry','Wild Cherry'),
('Cherry','Prunus avium'),
('Elm','English Elm'),
('Elm','Wych Elm'),
('Hawthorn','Hawthorn'),
('Hawthorn','Midland Hawthorn'),
('Holly','Ilex aquifolium'),
('Oak','Common Oak'),
('Oak','Sessile Oak'),
('Oak','Quercus petraea'),
('Poplar','Black Poplar'),
('Poplar','Aspen Poplar'),
('Poplar','Populus tremula'),
('Pine','Pinus sylvestris'),
('Pine','Douglas Fir');
DROP TABLE IF EXISTS forest;
CREATE TABLE forest
(forest_name VARCHAR(20) NOT NULL PRIMARY KEY
,tree_group VARCHAR(12) NOT NULL
);
INSERT INTO forest VALUES
('poplar_forest1','Poplar'),
('poplar_forest2','Poplar'),
('cherry_forest1','Cherry'),
('cherry_forest2','Cherry');
DROP TABLE IF EXISTS forest_tree;
CREATE TABLE forest_tree
(forest_name VARCHAR(50)
,species VARCHAR(50)
,PRIMARY KEY(forest_name,species)
);
SELECT * FROM trees;
+------------+--------------------+
| tree_group | species |
+------------+--------------------+
| Alder | Alnus glutinosa |
| Poplar | Aspen Poplar |
| Birch | Betula pendula |
| Cherry | Bird Cherry |
| Poplar | Black Poplar |
| Oak | Common Oak |
| Pine | Douglas Fir |
| Birch | Downy Birch |
| Elm | English Elm |
| Ash | Fraxinus excelsior |
| Hawthorn | Hawthorn |
| Holly | Ilex aquifolium |
| Hawthorn | Midland Hawthorn |
| Pine | Pinus sylvestris |
| Poplar | Populus tremula |
| Cherry | Prunus avium |
| Oak | Quercus petraea |
| Oak | Sessile Oak |
| Birch | Silver Birch |
| Cherry | Wild Cherry |
| Elm | Wych Elm |
+------------+--------------------+
SELECT * FROM forest;
+----------------+------------+
| forest_name | tree_group |
+----------------+------------+
| cherry_forest1 | Cherry |
| cherry_forest2 | Cherry |
| poplar_forest1 | Poplar |
| poplar_forest2 | Poplar |
+----------------+------------+
SET NAMES utf8;
TRUNCATE forest_tree;
SET @forest_name = 'poplar_forest1';
SET @species = 'Populus tremula';
INSERT IGNORE INTO forest_tree (forest_name,species)
SELECT @forest_name
, @species
FROM forest f
JOIN trees t
ON t.tree_group = f.tree_group
WHERE forest_name = @forest_name
AND species = @species
LIMIT 1;
SELECT * FROM forest_tree;
+----------------+-----------------+
| forest_name | species |
+----------------+-----------------+
| poplar_forest1 | Populus tremula |
+----------------+-----------------+
SET @forest_name = 'poplar_forest1';
SET @species = 'Black Cherry';
INSERT IGNORE INTO forest_tree (forest_name,species)
SELECT @forest_name
, @species
FROM forest f
JOIN trees t
ON t.tree_group = f.tree_group
WHERE forest_name = @forest_name
AND species = @species
LIMIT 1;
SELECT * FROM forest_tree;
+----------------+-----------------+
| forest_name | species |
+----------------+-----------------+
| poplar_forest1 | Populus tremula |
+----------------+-----------------+
SET @forest_name = 'poplar_forest1';
SET @species = 'Populus tremula';
INSERT IGNORE INTO forest_tree (forest_name,species)
SELECT @forest_name
, @species
FROM forest f
JOIN trees t
ON t.tree_group = f.tree_group
WHERE forest_name = @forest_name
AND species = @species
LIMIT 1;
SELECT * FROM forest_tree;
+----------------+-----------------+
| forest_name | species |
+----------------+-----------------+
| poplar_forest1 | Populus tremula |
+----------------+-----------------+
SET @forest_name = 'poplar_forest1';
SET @species = 'Black Poplar';
INSERT IGNORE INTO forest_tree (forest_name,species)
SELECT @forest_name
, @species
FROM forest f
JOIN trees t
ON t.tree_group = f.tree_group
WHERE forest_name = @forest_name
AND species = @species
LIMIT 1;
SELECT * FROM forest_tree;
+----------------+-----------------+
| forest_name | species |
+----------------+-----------------+
| poplar_forest1 | Black Poplar |
| poplar_forest1 | Populus tremula |
+----------------+-----------------+
-- Tree type TYP exists.
--
tree_type {TYP}
PK {TYP}
-- Tree sub-type SUB of tree type TYP exists.
--
tree_sub {TYP, SUB}
PK {TYP, SUB}
FK {TYP} REFERENCES tree_type {TYP}
-- Tree TRE is of tree type TYP, sub-type SUB.
--
tree {TRE, TYP, SUB}
PK {TRE}
SK {TRE, TYP}
FK {TYP, SUB} REFERENCES tree_sub {TYP, SUB}
-- Forest FOR has only trees of type TYP.
--
forest {FOR, TYP}
PK {FOR}
SK {FOR, TYP}
FK {TYP} REFERENCES tree_type {TYP}
每棵树是至多一个森林的一部分。
-- Tree TRE, of tree type TYP, is part of forest FOR.
--
forest_has_tree {TRE, TYP, FOR}
PK {TRE}
FK1 {TRE, TYP} REFERENCES tree {TRE, TYP}
FK2 {FOR, TYP} REFERENCES forest {FOR, TYP}
注:
All attributes (columns) NOT NULL
PK = Primary Key
AK = Alternate Key (Unique)
SK = Proper Superkey (Unique)
FK = Foreign Key
我设计这个数据库是为了将树木添加到森林中,每个森林只允许一种树。我想确保通过“forest_has_tree”table 添加到森林中的所有树木都属于该森林的正确类型。
我建议的设计是在“forest_has_tree”table 中包含“tree_type_idtree_type”字段,它引用“森林”中的“tree_type_idtree_type”table 以及“树”中的“tree_type_idtree_type”table(通过“tree_sub_type”table)。
CONSTRAINT `fk_forest_has_tree_forest1`
FOREIGN KEY (`forest_idforest` , `tree_type_idtree_type`)
REFERENCES `forest` (`idforest` , `tree_type_idtree_type`),
CONSTRAINT `fk_forest_has_tree_tree1`
FOREIGN KEY (`tree_idtree` , `tree_tree_sub_type_idtree_sub_type` , `tree_type_idtree_type`)
REFERENCES `tree` (`idtree` , `tree_sub_type_idtree_sub_type` , `tree_sub_type_tree_type_idtree_type`),
谁能评论一下这是否是一个可接受的table设计,或者是否有更好的方法来解决这个问题? 我在下面包含了这五个 table 的完整 MySQL。
我绝对相信没有森林会有超过一种树种。为了争论起见,假设我的森林都是 运行 独裁统治,如果有人种植未经授权的树,他们将与树一起蒸发,而且最重要的是不会保留任何记录。
-- -----------------------------------------------------
-- Table `tree_type`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `tree_type` (
`idtree_type` INT NOT NULL,
PRIMARY KEY (`idtree_type`))
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `forest`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `forest` (
`idforest` INT NOT NULL,
`tree_type_idtree_type` INT NOT NULL,
PRIMARY KEY (`idforest`, `tree_type_idtree_type`),
INDEX `fk_forest_tree_type1_idx` (`tree_type_idtree_type` ASC) VISIBLE,
CONSTRAINT `fk_forest_tree_type1`
FOREIGN KEY (`tree_type_idtree_type`)
REFERENCES `tree_type` (`idtree_type`)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `tree_sub_type`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `tree_sub_type` (
`idtree_sub_type` INT NOT NULL,
`tree_type_idtree_type` INT NOT NULL,
PRIMARY KEY (`idtree_sub_type`, `tree_type_idtree_type`),
CONSTRAINT `fk_tree_sub_type_tree_type1`
FOREIGN KEY (`tree_type_idtree_type`)
REFERENCES `tree_type` (`idtree_type`)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `tree`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `tree` (
`idtree` INT NOT NULL,
`tree_sub_type_idtree_sub_type` INT NOT NULL,
`tree_sub_type_tree_type_idtree_type` INT NOT NULL,
PRIMARY KEY (`idtree`, `tree_sub_type_idtree_sub_type`, `tree_sub_type_tree_type_idtree_type`),
INDEX `fk_tree_tree_sub_type1_idx` (`tree_sub_type_idtree_sub_type` ASC, `tree_sub_type_tree_type_idtree_type` ASC) VISIBLE,
CONSTRAINT `fk_tree_tree_sub_type1`
FOREIGN KEY (`tree_sub_type_idtree_sub_type` , `tree_sub_type_tree_type_idtree_type`)
REFERENCES `tree_sub_type` (`idtree_sub_type` , `tree_type_idtree_type`)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `forest_has_tree`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `forest_has_tree` (
`forest_idforest` INT NOT NULL,
`tree_type_idtree_type` INT NOT NULL,
`tree_idtree` INT NOT NULL,
`tree_tree_sub_type_idtree_sub_type` INT NOT NULL,
PRIMARY KEY (`forest_idforest`, `tree_type_idtree_type`, `tree_idtree`, `tree_tree_sub_type_idtree_sub_type`),
INDEX `fk_forest_has_tree_tree1_idx` (`tree_idtree` ASC, `tree_tree_sub_type_idtree_sub_type` ASC, `tree_type_idtree_type` ASC) VISIBLE,
INDEX `fk_forest_has_tree_forest1_idx` (`forest_idforest` ASC, `tree_type_idtree_type` ASC) VISIBLE,
CONSTRAINT `fk_forest_has_tree_forest1`
FOREIGN KEY (`forest_idforest` , `tree_type_idtree_type`)
REFERENCES `forest` (`idforest` , `tree_type_idtree_type`)
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `fk_forest_has_tree_tree1`
FOREIGN KEY (`tree_idtree` , `tree_tree_sub_type_idtree_sub_type` , `tree_type_idtree_type`)
REFERENCES `tree` (`idtree` , `tree_sub_type_idtree_sub_type` , `tree_sub_type_tree_type_idtree_type`)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
在我看来,您的架构只需要以下内容(为简洁起见省略了 FK 约束和代理 ID)...
编辑以添加一个工作示例...
DROP TABLE IF EXISTS trees;
CREATE TABLE trees
(tree_group VARCHAR(12) NOT NULL
,species VARCHAR(50) NOT NULL PRIMARY KEY
);
INSERT INTO trees VALUES
('Alder','Alnus glutinosa'),
('Ash','Fraxinus excelsior'),
('Birch','Downy Birch'),
('Birch','Silver Birch'),
('Birch','Betula pendula'),
('Cherry','Bird Cherry'),
('Cherry','Wild Cherry'),
('Cherry','Prunus avium'),
('Elm','English Elm'),
('Elm','Wych Elm'),
('Hawthorn','Hawthorn'),
('Hawthorn','Midland Hawthorn'),
('Holly','Ilex aquifolium'),
('Oak','Common Oak'),
('Oak','Sessile Oak'),
('Oak','Quercus petraea'),
('Poplar','Black Poplar'),
('Poplar','Aspen Poplar'),
('Poplar','Populus tremula'),
('Pine','Pinus sylvestris'),
('Pine','Douglas Fir');
DROP TABLE IF EXISTS forest;
CREATE TABLE forest
(forest_name VARCHAR(20) NOT NULL PRIMARY KEY
,tree_group VARCHAR(12) NOT NULL
);
INSERT INTO forest VALUES
('poplar_forest1','Poplar'),
('poplar_forest2','Poplar'),
('cherry_forest1','Cherry'),
('cherry_forest2','Cherry');
DROP TABLE IF EXISTS forest_tree;
CREATE TABLE forest_tree
(forest_name VARCHAR(50)
,species VARCHAR(50)
,PRIMARY KEY(forest_name,species)
);
SELECT * FROM trees;
+------------+--------------------+
| tree_group | species |
+------------+--------------------+
| Alder | Alnus glutinosa |
| Poplar | Aspen Poplar |
| Birch | Betula pendula |
| Cherry | Bird Cherry |
| Poplar | Black Poplar |
| Oak | Common Oak |
| Pine | Douglas Fir |
| Birch | Downy Birch |
| Elm | English Elm |
| Ash | Fraxinus excelsior |
| Hawthorn | Hawthorn |
| Holly | Ilex aquifolium |
| Hawthorn | Midland Hawthorn |
| Pine | Pinus sylvestris |
| Poplar | Populus tremula |
| Cherry | Prunus avium |
| Oak | Quercus petraea |
| Oak | Sessile Oak |
| Birch | Silver Birch |
| Cherry | Wild Cherry |
| Elm | Wych Elm |
+------------+--------------------+
SELECT * FROM forest;
+----------------+------------+
| forest_name | tree_group |
+----------------+------------+
| cherry_forest1 | Cherry |
| cherry_forest2 | Cherry |
| poplar_forest1 | Poplar |
| poplar_forest2 | Poplar |
+----------------+------------+
SET NAMES utf8;
TRUNCATE forest_tree;
SET @forest_name = 'poplar_forest1';
SET @species = 'Populus tremula';
INSERT IGNORE INTO forest_tree (forest_name,species)
SELECT @forest_name
, @species
FROM forest f
JOIN trees t
ON t.tree_group = f.tree_group
WHERE forest_name = @forest_name
AND species = @species
LIMIT 1;
SELECT * FROM forest_tree;
+----------------+-----------------+
| forest_name | species |
+----------------+-----------------+
| poplar_forest1 | Populus tremula |
+----------------+-----------------+
SET @forest_name = 'poplar_forest1';
SET @species = 'Black Cherry';
INSERT IGNORE INTO forest_tree (forest_name,species)
SELECT @forest_name
, @species
FROM forest f
JOIN trees t
ON t.tree_group = f.tree_group
WHERE forest_name = @forest_name
AND species = @species
LIMIT 1;
SELECT * FROM forest_tree;
+----------------+-----------------+
| forest_name | species |
+----------------+-----------------+
| poplar_forest1 | Populus tremula |
+----------------+-----------------+
SET @forest_name = 'poplar_forest1';
SET @species = 'Populus tremula';
INSERT IGNORE INTO forest_tree (forest_name,species)
SELECT @forest_name
, @species
FROM forest f
JOIN trees t
ON t.tree_group = f.tree_group
WHERE forest_name = @forest_name
AND species = @species
LIMIT 1;
SELECT * FROM forest_tree;
+----------------+-----------------+
| forest_name | species |
+----------------+-----------------+
| poplar_forest1 | Populus tremula |
+----------------+-----------------+
SET @forest_name = 'poplar_forest1';
SET @species = 'Black Poplar';
INSERT IGNORE INTO forest_tree (forest_name,species)
SELECT @forest_name
, @species
FROM forest f
JOIN trees t
ON t.tree_group = f.tree_group
WHERE forest_name = @forest_name
AND species = @species
LIMIT 1;
SELECT * FROM forest_tree;
+----------------+-----------------+
| forest_name | species |
+----------------+-----------------+
| poplar_forest1 | Black Poplar |
| poplar_forest1 | Populus tremula |
+----------------+-----------------+
-- Tree type TYP exists.
--
tree_type {TYP}
PK {TYP}
-- Tree sub-type SUB of tree type TYP exists.
--
tree_sub {TYP, SUB}
PK {TYP, SUB}
FK {TYP} REFERENCES tree_type {TYP}
-- Tree TRE is of tree type TYP, sub-type SUB.
--
tree {TRE, TYP, SUB}
PK {TRE}
SK {TRE, TYP}
FK {TYP, SUB} REFERENCES tree_sub {TYP, SUB}
-- Forest FOR has only trees of type TYP.
--
forest {FOR, TYP}
PK {FOR}
SK {FOR, TYP}
FK {TYP} REFERENCES tree_type {TYP}
每棵树是至多一个森林的一部分。
-- Tree TRE, of tree type TYP, is part of forest FOR.
--
forest_has_tree {TRE, TYP, FOR}
PK {TRE}
FK1 {TRE, TYP} REFERENCES tree {TRE, TYP}
FK2 {FOR, TYP} REFERENCES forest {FOR, TYP}
注:
All attributes (columns) NOT NULL
PK = Primary Key
AK = Alternate Key (Unique)
SK = Proper Superkey (Unique)
FK = Foreign Key