简单的表设计问题

时间:2010-02-02 17:04:07

标签: sql ruby-on-rails database-design postgresql

我正在努力提前思考,如果可能的话,尽量避免自己额外的痛苦。

我在过去的申请中遇到过这个问题,并且通常选择了最详细的方法,但是想要其他几个人的意见。

如果您有一个如下所示的基本表,那么包含一个字段是明智的和/或更有效的,该字段包括可以从另外两列中找到的信息的计算。 IE:

+-----+---------+------------+-------+--------+-------+
| id  | room_id | bookdate   | price | people | total |
+-----+---------+------------+-------+--------+-------+
| 414 | 132     | 2010-03-01 | 14.55 | 2      | 29.10 |
| 415 | 132     | 2010-03-02 | 14.55 | 2      | 29.10 |
| 416 | 132     | 2010-03-03 | 14.55 | 2      | 29.10 |
+-----+---------+------------+-------+--------+-------+

最后一个字段中的信息可以从前两个字段的产品中提取出来,因此它是多余的,不必要的。是否有可能值得拥有它的情况?

11 个答案:

答案 0 :(得分:6)

根据经验,我不会存储可以计算的值(特别是可以轻松计算的值),除非出现性能问题,我需要节省一些处理时间。

这是性能和存储之间的经典权衡。我建议计算值,直到你需要提升性能。

答案 1 :(得分:4)

也许创建一个包含除最后一个之外的所有字段的表,然后创建一个包含所有字段并自动计算最后字段的视图?

因此表格只包含这些字段

+-----+---------+------------+-------+--------+
| id  | room_id | bookdate   | price | people | 
+-----+---------+------------+-------+--------+
| 414 | 132     | 2010-03-01 | 14.55 | 2      | 

视图的定义,计算总数也很简单:

select *, price*people as total  from rooms

(假设您的表名为rooms

答案 2 :(得分:2)

一般规则是你不应该存储你可以轻松计算的内容,但如果你已经将这个区域确定为性能瓶颈 - 通过分析你的应用程序,而不是猜测 - 然后去做。

答案 3 :(得分:2)

如果您决定对读取性能进行非规范化,则可以添加检查约束以强制执行一致性。

create table rooms (
    price numeric, 
    people numeric, 
    total numeric check (total=price*people));

这会增加插入和更新的开销。

答案 4 :(得分:2)

我经常支持计算字段,假设您通过在计算数据库中定义字段来正确执行此操作。这样,无论数据如何变化,计算始终适用。如果你需要在包含许多记录的报告中进行这些计算,我只会这样做。当然,在查询中编写公式很容易,但是如果经常计算这个数字就会浪费服务器资源(计算字段仅在信息发生变化时执行计算),如果必须对数百万计算结果,可能会严重减慢查询速度报告记录。物化视图也是一个好主意(因为它会预先计算),但是常规视图只会让你多次编写calc,它没有计算字段的性能优势。另一方面,如果我不需要(即我可以通过其他方式解决问题),我从不创建视图,因为当人们开始在视图上创建视图时,它们会让您陷入真正的性能问题。当您需要螺丝刀时,请不要使用锤子。

如果使用得当,计算字段是功能强大的工具,数据库设计人员经常会忽略这些工具。

答案 5 :(得分:1)

如果您在编写查询时为方便起见,我会创建一个包含总数的视图。

否则,这是normalization的问题。有时对表进行非规范化是可以接受的。 Denormalization,尤其是在数据仓库等环境中,可用于提高性能。但是,确保数据保持一致非常重要。换句话说,当totalprice发生变化时,您需要确保people字段得到更新。

在实践中,我认为这是最后的手段,只有在其他性能优化不足时使用。另外,非规范化并不能保证改进 - 取决于数据量和其他因素,实际上可能会使事情变得更糟。

注意:在删除计算字段之前,表格不能是3NF(第三范式)。

答案 6 :(得分:1)

如果您担心选择性能(至少WHERE total = xx.xx),您只需添加一个索引。

CREATE INDEX booking_total ON预订((价格*人));

这会改变SELECT * from booking where price*people = 58.2;的查询计划;

Seq Scan on booking (cost=0.00..299.96 rows=60 width=24) (actual time=0.015..2.926 rows=1 loops=1) Filter: ((price * (people)::double precision) = 58.2::double precision) Total runtime: 2.947 ms

到这个

Bitmap Heap Scan on booking (cost=4.30..20.83 rows=5 width=24) (actual time=0.016..0.016 rows=1 loops=1) Recheck Cond: ((price * (people)::double precision) = 58.2::double precision) -> Bitmap Index Scan on booking_total (cost=0.00..4.29 rows=5 width=0) (actual time=0.009..0.009 rows=1 loops=1) Index Cond: ((price * (people)::double precision) = 58.2::double precision) Total runtime: 0.044 ms

PostgreSQL摇滚: - )

答案 7 :(得分:0)

我会继续进入TOTAL字段。从我在这里可以看到,没有'折扣'或类似的字段可能会减少总数,但我可以想象价格*人数可能不等于总数的情况。您可能需要考虑一个COMMENTS字段甚至一个表格,以允许某人注意为什么总数与其他字段的产品不匹配。

分享并享受。

答案 8 :(得分:0)

基本上我不希望有一个“总计”字段,或者由其他字段计算的任何字段,不在同一个表中,也不在其他表中。 如果价格字段发生变化,有人可能会“忘记”更新总字段,最终会得到错误的数据。

使用此字段进行SELECT非常容易: SELECT price,people,(price * people)AS total FROM some_table;

我认为保持计算字段的唯一情况是计算字段需要很长时间,并且会使数据库超载大量数据。

BR

答案 9 :(得分:0)

通常认为存储可以从表中的其他字段简单计算的字段是不好的做法。我建议的唯一一次是,当您需要存储复杂计算的结果时,存储计算值比每次重新计算值更容易 - 但在您的情况下,这似乎不是必需的。 / p>

计算字段的另一个问题是,可以在不修改存储结果的情况下更改用于计算的原始值,从而导致应用程序中出现潜在问题。

答案 10 :(得分:0)

在这种情况下,你可以很容易地计算出这个值 - 这是多余的。您几乎不应该存储冗余数据。这意味着您更新价格或人员的每个地方,您必须确保更新总数。如果您忘记在一个地方执行此操作,则数据现在不一致。因此,假设您现在有一条记录表示价格= 10美元,人员= 3,总计= 40美元。如果您有不同的程序以不同的方式显示信息 - 不同的总数或子集或其他 - 用户可以根据他的要求获得相同问题的不同答案。虽然得到一个错误答案是不好的,但有时候得到一个正确的答案,有时候是一个错误的答案更糟糕,因为那时可能不清楚如何解决问题。我的意思是,如果我看到某个客户在他应该显示3时显示2个人,可能有一些我可以去的屏幕,用3改写2,点击保存或其他什么,并且它是固定的。但是,如果它说10美元2人= 30美元,我在哪里去修理它?怎么样?

您可能会说记录只在一个地方更新,所以没有问题。但那就是今天。如果明天您或其他程序员添加新功能以进行不同类型的更新会怎么样?

我正在研究一个充满冗余数据的系统。有关我们公司每个产品的基本信息存储在“项目”表中。对于库存中的每个单元,我们有一个库存记录,而不是简单地引用项目记录,它们复制每个库存单元的所有数据。销售商品时,我们会将所有数据复制到销售记录中。如果返回了某些内容,我们会将所有数据复制到返回记录中。等等其他几种记录类型。这会造成无穷无尽我们曾经遇到过一个问题,即用户运行查询以查找具有特定特征的项目,并且命中列表包含不符合搜索条件的项目。为什么?因为查询查找满足搜索条件的所有项目记录,所以尝试将这些项目记录与库存记录按部件号匹配...但由于各种原因,某些库存记录与其他条件上的项目记录不匹配。现在我正在努力解决一个问题,即成本数据并不总是正确地从库存记录复制到销售记录。我想重新设计数据库以消除所有冗余数据,但这将是一个巨大的项目。

当然,有时候重新计算某些数据的性能损失太高了。比如,如果您需要读取数以千计的交易记录来计算当前余额,并且您经常想要显示当前余额,那可能只是太大的性能负担而且您最好将其冗余存储。但是做这种事我会很慢。确保它确实是一个严重的性能问题。

将您正在阅读的记录中的两个数字相乘?没门。我无法想象这会导致任何性能问题。如果您的数据库引擎无法在读取记录所用时间的一小部分时间内乘以两个数字,那么请使用新的数据库引擎。