Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/84.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Mysql 一列指向多个表的表的模式设计_Mysql_Sql_Mysql Workbench - Fatal编程技术网

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匹配

  • 若并没有公共属性,那个么这种设计是不相关的,所以我在主表中放了一个公共数据字段

  • 我通过定义外键来实现关系

查询示例:

我想要id 1的公共数据字段:

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