2010-02-02 47 views
7

我想要提前一点,并尽可能避免自己一些额外的痛苦。简单的桌子设计问题

我在过去的应用程序中遇到了这个问题,并且通常选择了最详细的方法,但希望其他几个人的意见。

如果您有一个如下所示的基本表格,明智的和/或更有效的方法是包含一个字段,其中包含可从其他两列中找到的信息的计算。 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 | 
+-----+---------+------------+-------+--------+-------+ 

在最后一个字段的信息可以从以前二者的乘积中提取,因此它是多余的和不必要的。有没有什么情况下仍然值得拥有它?

+0

这也是一个项目与Rails的...总数列可以在轨道中总结做... Table.sum('total'),但是找到两个字段的乘积的SUM将不会这种内置方法。 ;-( – holden 2010-02-02 17:07:09

回答

6

作为一个经验法则,我不会存储可以计算的值(特别是可以轻松计算的值),除非有ap性能问题,我需要节省一些处理时间。

这是性能和存储之间的经典平衡。我会推荐计算这个值直到你需要提升性能。

4

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

所以该表将只包含这些字段

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

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

select *, price*people as total from rooms 

(假设你的表称为rooms

0

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

分享和享受。

2

一般的规则是,你不应该存储什么,你可以很容易地计算,但如果你已经通过剖析你的应用程序识别这个领域作为一个性能瓶颈—,而不是凭空猜测—然后再去做。

0

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

使用此字段进行选择非常容易: 选择价格,人员,(价格*人)AS总数FROM some_table;

唯一的情况下,我想可以保留一个计算字段是需要很长时间来计算它,它会在数据库上的海量数据超载。

BR

0

它通常被认为是不好的做法,存储,可以从您的表中其他领域简单地计算领域。只有当我需要存储复杂计算的结果并且存储计算值比每次重新计算值时更容易 - 但在您的情况下,这似乎不是必要的。

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

1

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

否则,这是一个normalization的问题。有时非规范化表格是可以接受的。可以使用Denormalization,尤其是在像数据仓库这样的环境中提高性能。但是,确保数据保持一致非常重要。换句话说,当pricepeople更改时,您需要确保您的total字段得到更新。

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

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

0

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

你可能会说记录只是在一个地方更新,所以没有问题。但今天就是这样。如果明天你或者其他一些程序员增加了一个新的功能来做不同的更新呢?

我正在使用冗余数据填充系统。关于我们公司每个产品的基本信息都存储在“项目”表中。对于库存中的每个单位,我们都有一个库存记录,而不是简单地参考物料记录,而是复制每个库存单位的所有数据。当一件物品被出售时,我们将所有数据复制到销售记录中。如果返回了某些内容,我们会将所有数据复制到返回记录中。等等其他几种记录类型。这会造成无尽的麻烦。我们曾经遇到过一个问题,用户运行查询查找具有某些特征的项目,而点击列表中包含不符合搜索条件的项目。为什么?由于查询查找到符合搜索条件的所有条目记录,该条目试图通过零件号码将这些条目记录与库存记录进行匹配......但由于各种原因,某些库存记录与其他条件中的条目记录不匹配。目前,我正在努力解决一个问题,即费用数据并不总是从库存记录正确复制到销售记录。我很想重新设计数据库以消除所有冗余数据,但这将是一个巨大的项目。

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

将两个数字加在一起,并且记录在您已经阅读的记录中?没门。我无法想象这会导致任何性能问题。如果您的数据库引擎无法在读取记录所花费的时间的很小一部分时间内将两个数字相乘,请获取新的数据库引擎。

2

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

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

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

1

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

CREATE INDEX booking_total ON预订((price * people));

这将从此更改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的岩石:-)

2

我经常赞成计算字段假设你这样做是正确的在定义字段数据库计算。这样无论数据如何变化,计算总是适用的。我只会这样做,但如果您需要在包含多条记录的报告中获得这些计算结果。当然,在查询中编写公式很容易,但如果频繁计算此数字,则会浪费服务器资源(计算字段只在信息更改时执行计算),并且如果必须执行数百万的计算,可能会严重降低查询速度的报告记录。物化视图也是一个好主意(因为它会预先计算),但常规视图会让您无法多次编写计算,它不具有计算字段的性能优势。另一方面,如果我不需要(即,我可以用其他方式解决问题),我就不会创建视图,因为当人们开始在视图之上创建视图时,它们可能会使您陷入真正的性能问题。当螺丝刀是你需要的时候不要使用锤子。

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