我在operation
数据库中有两个表source
和mysql
。
在operation
我有10行(可能性),source
只有3行(可能性),在它们之间存在many-to-many
关系。
问题:是否需要添加此额外表格,或者只需在source
中添加operation
的外键。
operation
可以是订阅请求,订阅启用,订阅禁用,订阅取消,付款确定,订阅交易确定,订阅开始。
source
可以来自互联网,来自代理商
来源有共同的操作和独立的操作。
操作subscribe enabled
可以通过互联网订阅或代理订阅和操作来完成:subscribe deal ok
可以仅来自代理,subscribe request
可以来自互联网。
答案 0 :(得分:7)
在关系数据库中,您需要3个表来建立多对多的关系。两个包含主键和连接表。别无他法。
答案 1 :(得分:6)
对于简短而简短的回答,通常,对于像mysql这样的rdbms,只支持一对多关系,你需要第3个(交叉或交叉引用)表来实现多对多两个实体之间的关系。
<强>可是... 强>
由于您没有太多记录,因此您可以将source
和operation
之间的多对多关系映射到仅一个附加列 source
并且没有冗余数据存储。但是,你可能会失去一些性能(例如:不那么强大的索引),并且肯定会让你的生活更难以使用这些表......
诀窍是在operation
表中使用特定的二进制值作为主键值,并在source
表中添加一个整数列,在该表中使用其位来映射关系。因此,本专栏的一部分描述了实际源记录与相应操作记录之间的一种关系。
对于样本operation
表,您可以创建一个表格,其中包含bit
类型的pri键,其大小等于您估计的行数。你说你将有~10行,所以使用bit(10)
作为数据类型。因为mysql会将int存储在4个字节上,所以你不必在这里放松存储大小(相反,与int相比,你实际上可能会赢得一些,但这实际上是dbe如何能够压缩实际上,如果你愿意的话,你也可以简单地使用int。)
create table operation (id bit(10) primary key, title varchar(50));
insert into operation values (b'0', 'none');
insert into operation values (b'1', 'subscribe request');
insert into operation values (b'10', 'subscribe enabled');
insert into operation values (b'100', 'subscribe disabled');
insert into operation values (b'1000', 'subscribe canceled');
insert into operation values (b'10000', 'payment ok');
insert into operation values (b'100000', 'subscribe deal ok');
insert into operation values (b'1000000', 'subscribe start');
现在,假设您的源表中包含以下内容:
create table source (id int primary key, value int, operations bit(10));
insert into source values (1, 1, b'0');
insert into source values (2, 2, b'1'); -- refers to subscribe request
insert into source values (3, 3, b'10'); -- refers to subscribe enabled
insert into source values (4, 4, b'10011'); -- refers to payment ok, subscribe request, subscribe enabled
insert into source values (5, 5, b'1110011'); -- refers to subscribe deal ok, subscribe start, payment ok, subscribe request, subscribe enabled
现在,如果您希望选择所有关系,请按以下方式连接这两个表:
select source.id, operation.title
from source
join operation
on (source.operations & operation.id);
id operation.title
2 subscribe request
4 subscribe request
5 subscribe request
3 subscribe enabled
4 subscribe enabled
5 subscribe enabled
4 payment ok
5 payment ok
5 subscribe deal ok
5 subscribe start
如果您希望添加新关系,您可以利用插入的on duplicate key update条款,因此您不必担心现有关系:
insert into source (id,value,operations)
values (2,2,(select id from operation where title = 'subscribe start'))
on duplicate key update operations = operations
| (select id from operation where title = 'subscribe start');
如果您希望删除关系:
update source set operations = operations
& ~(select id from operation where title = 'subscribe start') where source.id=2;
总而言之,这不是一个不错的选择,而是将多对多关系映射到两个表格的可能方式。
答案 2 :(得分:2)
根据您的实际需要,您的问题可以有很多答案。
实际上,在描述的情况下,您可以只使用一个表“操作”,并将source
列定义为MySQL SET
类型。然后,您将能够为每个操作选择0到多个来源。
然后,您可以更改表操作以设置源列
ALTER TABLE operation ADD source SET('from internet', 'from agent');
如果你真的需要有两个表(假设你的“源”表包含其他字段),你应该有第三个表来建立它们之间的关系。
但是,从技术上讲,有些情况下,出于性能原因,您可能更喜欢将外键存储在其中一个表的varchar()字段中,并使用逗号分隔符,并使用PHP来检索数据。但这并不是做到这一点的好方法,尽管是是可能的,只要你的数据检索只在一个方向完成,而你真的确定你在做什么。
例如,在这种“hacky-way”中,您可以想象一个类似ActiveRecord的PHP类,您可能希望使用这样的方法检索您的源
public function getSources() {
private $_sources;
if (!isset($this->_sources)) {
$this->_sources=DBSource::findByPks(explode(",", $this->sources));
}
return $this->_sources;
}
答案 3 :(得分:1)
根据您描述的问题,您似乎不一定要有多对多的关系,因为 来源&#39;和&#39; 操作&#39;是枚举(一组常量值)。因此,&#39; 来源&#39;和&#39; 操作&#39;不要作为一个表,而是作为数据类型(即列类型)。
您可以查看Enums in mySQL并创建自己的&#39; 来源&#39;和&#39; 操作&#39;枚举并将它们放入一个表格中,以保持这种“多对多关系”#34;。
请记住,对于我提议的解决方案,我假设<#39; 来源&#39;和&#39; 操作&#39;拥有一组恒定且已知的值。如果不是这样,那么你会遇到麻烦,就像你有一个非规范化的数据库一样。
答案 4 :(得分:1)
我建议你采取最简单的方法解决问题,通常是最好的方法。只有在真正需要时才使用多对多关系。
您写道:
在源中只有3行(可能性)
来源可以来自互联网,来自代理商
这只是两个选择。
为什么没有source
这样:
基本上,如果您非常确定source
s的集合不会增长,您可以对所有变体进行硬编码。它以这种方式进行了优化,但是你失去了灵活类似于@lp_的答案。
答案 5 :(得分:1)
如果您知道在源表中最多有3行,您可以使用如下所示的操作表将关系映射到3(而不是多对多)
operation
---------
id_source_1
id_source_2
id_source_3
如果您不知道源中有多少行,则需要第三个表,因为只有第三个表才能映射多对多关系。