Mysql 一列指向多个表的表的模式设计
考虑以下模式:Mysql 一列指向多个表的表的模式设计,mysql,sql,mysql-workbench,Mysql,Sql,Mysql Workbench,考虑以下模式: create schema testSchema; use testSchema; create table A ( id int not null auto_increment, name varchar (50), primary key(id)); create table B ( id int not null auto_increment, name varchar (50), primary key(id)); c
create schema testSchema;
use testSchema;
create table A (
id int not null auto_increment,
name varchar (50),
primary key(id));
create table B (
id int not null auto_increment,
name varchar (50),
primary key(id));
create table C (
id int not null auto_increment,
name varchar (50),
primary key(id));
create table main (
id int not null auto_increment,
typeId int,
type varchar(50),
tableMappingsId int,
primary key (id)
);
create table tableMappings (
id int not null auto_increment,
tableName varchar(50),
primary key (id)
);
insert into A (name) values
('usa'),
('uk'),
('uno');
insert into B (name) values
('earth'),
('mars'),
('jupiter');
insert into C (name) values
('1211'),
('12543'),
('345');
insert into main (typeId, type, tableMappingsId) values
(1,'tableA',1),
(2,'tableB',2),
(3,'tableC',3);
insert into tableMappings (tableName) values ('A'),('B'),('C');
说明:-
我有三个表A、B和C,它们有id和名称
在主表中,type告诉我们必须从哪个表(A、B或C)读取name属性。typeId告诉表中的id(A、B或C)。为此,我创建了一个具有表名的tableMappings表。在主表中,我创建了一列tableMappingsId,它指向tableMappings
这是正确的方法吗?如何在MySQL中编写如下查询:-
从此行指向并由tableMappings映射的表中选择(名称属性) 关于您的设计 如果我们以面向对象的方式思考,您的基类由记录在主表中的属性组成,并由a、B和C派生出附加属性。 您希望避免在一个表中包含多个属性,这些属性根据记录类型的不同使用空值。这是一个好办法。但是您实现这一点的方法可以改进 回答您的问题 您希望根据字段的值从表(a、B或C)中进行选择。据我所知,如果不“准备”查询,这是不可能做到的 “准备”查询可以通过多种方式完成:
- 使用准备好的语句(“纯SQL”方法):
- 在存储过程或函数中,例如:选择类型,然后测试类型并从正确的表中选择
- 或者通过脚本语言两次构造查询
SET @idToSelect = 2;
SELECT
CONCAT('SELECT name FROM ', tableMappings.tableName, ' WHERE Id = ', main.tableMappingsId)
INTO @statement
FROM main
INNER JOIN tableMappings ON tableMappings.tableName = REPLACE(main.type, 'table', '')
WHERE main.id = @idToSelect;
PREPARE stmt FROM @statement;
EXECUTE stmt;
注:我们必须翻译“表A”,“表B”。。。在main.type中匹配“A”、“B”。。。在tableMappings.tableName中,这是不理想的
但这不是很方便和有效
其他方法和评论
从多个表中选择:不一定是什么大问题
基本上,您希望避免从不需要阅读的表中进行选择。但是请记住,如果您的模式被正确地索引,这不一定是一件大事。MySQL运行一个查询优化器。您只需左键联接所有表,然后根据“类型”值从右表中选择:
SET @idToSelect = 2;
SELECT
IFNULL(A.name, IFNULL(B.name, C.name)) AS name
FROM main
LEFT JOIN A ON main.type = 'tableA' AND A.id = main.tableMappingsId
LEFT JOIN B ON main.type = 'tableB' AND B.id = main.tableMappingsId
LEFT JOIN C ON main.type = 'tableC' AND C.id = main.tableMappingsId
WHERE main.id = @idToSelect;
请注意,我没有使用tableMappings表
无用的桌面映射技巧
通过在“children”表中使用与主表中相同的id,可以避免使用这种映射。这是有多少ORM实现了继承。我将在稍后的回答中给出一个例子
一个有点不相关的例子
在您的问题中,您希望选择“name”属性,而不考虑记录的类型。但我敢打赌,如果你有真正不同类型的记录,每种类型都有一组不同的属性。如果“name”是所有类型之间的公共属性,那么它应该位于主表中。我假设您提供了“name”作为简化示例。
但我认为在实际情况中,无论对象的类型如何,都很少需要选择字段
其他事项:在数据示例中,您为A、B和C表提供了与主记录不匹配的记录
最终命题
一些评论:
- 主表中的typeId是可选的,但根据您必须执行的查询,检索对象的类型可能会很有用。一个字段就足够了,不需要typeId integer和类型varchar
- A、B和C表中的id不是自动递增的,因为它们必须与主id匹配
- 若并没有公共属性,那个么这种设计是不相关的,所以我在主表中放了一个公共数据字段
- 我通过定义外键来实现关系
SELECT common_data FROM main WHERE id = 1;
我知道id 2来自类型B,我想要特定数据B:
SELECT specific_dataB FROM B WHERE id = 2;
我知道id 3来自类型C,我想要公共数据和特定数据C:
SELECT common_data, specific_dataB FROM main INNER JOIN B ON B.id = main.id WHERE main.id = 2;
(最符合您的情况)我不知道对象3的类型,但我希望根据其类型获得特定数据:
SELECT IFNULL(
A.specific_dataA,
IFNULL(
B.specific_dataB,
C.specific_dataC
)
)
FROM main
LEFT JOIN A on A.id = main.id
LEFT JOIN B on B.id = main.id
LEFT JOIN C on C.id = main.id
WHERE main.id = 3
基本上,为什么不将名称varchar(50)放在主表中呢?是什么激发了您的设计?表A、B和C可以有许多记录,并且其中还包含多个其他列。name属性可以是varchar(200)或更大。所以基本上动机和我们为什么使用外键是一样的。所以如果我们以面向对象的方式思考,你有一个基类,它由记录在主表中的属性组成,并由a、B和C派生,带有附加属性?是的,主类有许多其他属性,加上X属性,可以是a、B或COk。我准备了一个答案
SELECT IFNULL(
A.specific_dataA,
IFNULL(
B.specific_dataB,
C.specific_dataC
)
)
FROM main
LEFT JOIN A on A.id = main.id
LEFT JOIN B on B.id = main.id
LEFT JOIN C on C.id = main.id
WHERE main.id = 3