我继承了一个庞大的现有数据库,我想知道我是否应该重构它,因为95%的查询需要加入至少4个表。
数据库有5个表,只有一个ID和Name列少于20行。我假设作者这样做,所以他可以更改那里的名称,而不是在其他表中更改它们,但其中许多表只在另一个表中引用。我应该将这些小的2列表重构为更大的表并向列添加约束,以便用户不能输入错误的名称而不是具有单独的表吗?
答案 0 :(得分:8)
抵制这种冲动。从您的描述中我可以推断出现有设计是可靠的并且可能已经很好地标准化。 您的重构实际上可能会撤消一个好的数据库结构。
如果您在查询中编写了大量连接,我会建议创建视图以减轻样板。
......作者这样做了,所以他可以改变那里没有变化的名字 他们在其他表格中......
这是良好设计的证据,正是您应该在规范化数据库中努力的目标。
答案 1 :(得分:3)
没有。
您的数据库已正常化且正确。 并节省空间,查找时间,索引以存储int而不是varchar名称
如果正确键入小表,则会对其进行优化。
答案 2 :(得分:1)
听起来就像查找表一样。让我告诉你,当你决定将所有查找放在一个带有附加列的表中以指定它是什么类型时,就会发生这种情况。 Fisrt而不是在一个查询中加入4个不同的表,您必须连接到同一个表4次。最终会在“一个表中统治所有人”中对资源进行更多争用。此外,您将失去FK约束。这意味着您最终会失去数据完整性。因此,如果一个查找是状态,则不会阻止您在customeraddress表的stateid列中为客户类型添加不同查找的id值。当查找是分开的时,您会强制执行该关系。
假设您决定对客户类型的列进行约束,而不是使用一个大表。现在强制执行约束,但是当他们需要更改时你会遇到问题。现在,您必须更改数据库才能添加新类型。通常,当桌子变大时,这是一个非常糟糕的想法。
答案 3 :(得分:1)
短篇小说:用ID号替换字符串与规范化无关。在您的情况下使用自然键可能会提高性能。 在我的测试中,使用自然键的查询速度提高了1或2个数量级。
您可能过快地接受了答案。
数据库有5个表,只有ID和Name列少 超过20行。
我假设这些表格的结构类似于此。
create table a (
a_id integer primary key,
a_name varchar(30) not null unique
);
create table b (...
-- Just like a
create table your_data (
yet_another_id integer primary key,
a_id integer not null references a (a_id),
b_id integer not null references b (b_id),
c_id integer not null references c (c_id),
d_id integer not null references d (d_id),
unique (a_id, b_id, c_id, d_id),
-- other columns go here
);
很明显,your_data需要四个连接(至少)才能从中获取有用的信息。
但是表a,b,c和d中的名称是唯一的(ahem),因此您可以使用唯一名称作为外键引用的目标。你可以重写像你这样的表。
create table your_data (
yet_another_id integer primary key,
a_name varchar(30) not null references a (a_name),
b_name varchar(30) not null references b (b_name),
c_name varchar(30) not null references c (c_name),
d_name varchar(30) not null references d (d_name),
unique (a_name, b_name, c_name, d_name),
-- other columns go here
);
用字符串替换id号不会改变正常形式。 (并且用id号替换字符串与规范化没有任何关系。)如果原始表是5NF,那么这个重写也将在5NF中。
但性能怎么样?是不是id数加连接应该比字符串更快?
我通过在四个表a,b,c和d中的每一个中插入20行来测试它。然后我生成了一个Cartesian产品来填充一个用id号写的测试表,另一个使用这些名称。 (所以,每行160K行。)我更新了统计数据,并运行了几个查询。
explain analyze
select a.a_name, b.b_name, c.c_name, d.d_name
from your_data_id
inner join a on (a.a_id = your_data_id.a_id)
inner join b on (b.b_id = your_data_id.b_id)
inner join c on (c.c_id = your_data_id.c_id)
inner join d on (d.d_id = your_data_id.d_id)
...
Total runtime: 808.472 ms
explain analyze
select a_name, b_name, c_name, d_name
from your_data
Total runtime: 132.098 ms
使用id号的查询需要花费更长的时间才能执行。我在所有四列上都使用了WHERE子句,它返回一行。
explain analyze
select a.a_name, b.b_name, c.c_name, d.d_name
from your_data_id
inner join a on (a.a_id = your_data_id.a_id and a.a_name = 'a one')
inner join b on (b.b_id = your_data_id.b_id and b.b_name = 'b one')
inner join c on (c.c_id = your_data_id.c_id and c.c_name = 'c one')
inner join d on (d.d_id = your_data_id.d_id and d.d_name = 'd one)
...
Total runtime: 14.671 ms
explain analyze
select a_name, b_name, c_name, d_name
from your_data
where a_name = 'a one' and b_name = 'b one' and c_name = 'c one' and d_name = 'd one';
...
Total runtime: 0.133 ms
使用身份证号码的表格花了大约100倍的时间来查询。
测试使用PostgreSQL 9.something。
我的建议:在购买之前先试试。我的意思是,在投资前进行测试。尝试重写数据表以使用自然键。仔细考虑ON UPDATE CASCADE
和ON DELETE CASCADE
。使用代表性样本数据测试性能。编辑您的原始问题,并告诉我们您找到的内容。