MySQL能自动透明地去重字符串吗?

3

在C语言中,编译器会为"字符串"分配数字ID(4字节指针),并且只保留每个字符串的一份拷贝:对于char *a="Hello", *b="Hello";,内存中只存储"Hello"的一个副本。这完全是自动的,对用户透明。

我的问题是,MySQL是否能够做到同样的事情,即自动透明地去重字符串。

理想情况下,我希望它成为数据库的内部存储机制,以便于(如C语言),对于用户而言,数据库看起来和表现得就好像它包含实际的字符串,而在实现上,它只包含指针。

在我的数据库中有很多重复的字符串,例如:

`unit`, `building`, `office`, `firstName`, `lastName`

Chicago main production unit    | headquarters | accounting | Jane | Smith
Chicago main production unit    | office       | sales      | Jane | Dow
Miami administrative department | headquarters | sales      | Mary | Smith
Miami administrative department | office       | accounting | Mary | Dow

等等。例如,像'迈阿密行政部门''会计''史密斯'这样的字符串在不同的记录中重复出现。

这会增加数据库的大小,以至于我会遇到托管限制。

一个显而易见的解决方案是数据规范化:为名称保留单独的表。

`id`, `string`

1 | Chicago main production unit
2 | Miami administrative department
3 | headquarters
4 | accounting
5 | Jane
6 | Smith
7 | office
8 | sales
9 | Dow

然后,将我的表格设置为

`unit_id`, `building_id`, `office_id`, `firstName_id`, `lastName_id`

1 | 3 | 4 | 5 | 6
1 | 7 | 8 | 5 | 9

并且翻译所有输入和输出的字符串。但是,这样做非常麻烦。

我的问题是MySQL是否可以自动透明地为用户执行此操作:每当我插入一行时,它会自动更新字符串表,并仅在表中存储ID而不是字符串,删除、WHERE等也是如此,以便于用户看起来,该表与具有字符串的表完全相同,但占用更少的空间。


听起来你想要规范化你的数据库,这样你就有了多个实体。一开始,你似乎有一个位置实体和一个人员实体。SQL通过使用数据建模为数据库创建正确的数据模型来处理这个问题。 - Gordon Linoff
@GordonLinoff 谢谢,我会研究规范化。不,我描述的结构只是一个例子;在我的应用程序中,我有文档标题、这些文档中的单词、这些单词的形态学形式、文档类型等。 - Alexander Gelbukh
1
一些关系型数据库管理系统具有类似的功能,例如SQL Server的最新版本中的页面压缩会使用字典压缩透明地压缩页面上重复的行数据,这在概念上与C字符串池并不相差太远。我想大多数服务器通常都以“快速”为目标而非“小型化”,并留给您手动决定如何最好地规范化您的数据。 - Matt Gibson
1个回答

1
我的问题是MySQL是否能做到同样的事情。 虽然您肯定可以实现所需的结果(它被称为数据规范化),但MySQL不会隐含地执行它。 MySQL能否自动透明地为用户执行此操作? 不,MySQL不能自动为您执行此操作-您必须自己执行。您需要在查询和DDL语句中明确说明它。 这里是一个简短的演示,展示如何创建查找表,然后在插入和选择中使用它:
create table lookup(id int, name varchar(10));
create table data(id int, id_lookup int);
insert into lookup(id,name) values (1,'quick');
insert into lookup(id,name) values (2,'brown');
insert into lookup(id,name) values (3,'fox');

insert into data (id, id_lookup)
values (110, (select id from lookup where name = 'quick'));
insert into data (id, id_lookup)
values (120, (select id from lookup where name = 'brown'));
insert into data (id, id_lookup)
values (130, (select id from lookup where name = 'quick'));
insert into data (id, id_lookup)
values (140, (select id from lookup where name = 'fox'));

现在,data 包含这些行:
110 1
120 2
130 1
140 3

要选择名称,您需要加入到您的查找表:

select d.id, t.name
from data d
join lookup t on t.id=d.id_lookup

在sqlfiddle上的演示。

注意:为所有字符串创建查找表并不常见。通常,您会为每种类型的字符串创建一个单独的查找表(例如unit_lookupbuilding_lookup等),或者使用特殊的查找代码列来分区查找表:

id code name
-- ---- ----
 1 unit Chicago
 2 unit Miami
 3 bldg Headquarters
 4 bldg Office

是的,这正是我在问题中所指的显而易见的解决方案。我的理解是,回答这个问题的答案很明显是“不”,MySQL不会透明地为我完成它?我可以像你(和我)描述的那样自己完成它,但语言本身没有提供具体的帮助?我希望的是表上的某些属性,然后就完成了。 - Alexander Gelbukh
澄清一下:我的问题不是我自己能否在MySQL中做到这一点,而是MySQL是否可以像C语言一样为我完成。答案显然是否定的,对吧? - Alexander Gelbukh
2
@AlexanderGelbukh 不,MySQL或任何主要的SQL引擎中都没有魔法可以自动化此过程。如果您尝试思考RDBMS需要透明地实现此操作,您很快就会遇到逻辑问题。例如,如果您将字符串“Office”更新为一行中的“Ufficio”,它会怎么做?它应该在所有引用“Office”的其他行中变为相同吗?当您删除最后一个包含特定值的行时会发生什么? - Sergey Kalinichenko
1
@dasblinkenlight:我认为数据库自动处理没有问题。当我将一行更新为“Ufficio”时,它应该在其内部名称表中插入“'Ufficio'”,并更新这一行,就像存储字符串一样。名称表应包含引用计数,当引用计数归零时,该值将被删除。Unix文件系统可以轻松完成此操作(我的意思是硬链接)。编程语言中的字符串更改延迟复制也可以轻松完成此操作。 - Alexander Gelbukh
1
接受答案。我仍然建议将“当然可以 - 这被称为数据规范化”更改为“不,它不能,但您可以自己完成 - 这被称为数据规范化”。 - Alexander Gelbukh
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接