如何正确设计一个sql数据库来使用聚合值?

时间:2017-01-23 09:35:58

标签: sql database-design

在以下示例中

Order
-------
ID (int)
CreatedAt (smalldatetime)
....

OrderItems
-------
ID (int)
OrderID (int)
Quantity (int)
UnitPrice (decimal)
CalculationUnit (int)
TotalItemPrice (decimal)
....

我有一个很大的dillema在哪里以及我应该如何跟踪TotalOrderPrice而我主要关心的是速度和数据的一致性。

a)TotalOrderPrice可以存储在表单中,并且应该针对相关OrderID的每个OrderItem更改进行更新

这是否会导致数据不一致,因为数据是"重复"?

b)我可以拥有一个可以包含总计TotalItemPriceValues的视图,例如

OrderTotal
------------
ID (int)
TotalOrderprice (decimal)

在扩展应用程序时这可能是一个潜在的问题吗?

c)或者我可以保留原始设计,并在业务逻辑中计算OrderTotalPrice。

这是否会降低性能,因为必须检索所有订单商品才能获得总订单价格?

我知道没有银子弹,但由于我没有大量的测试数据,我只是想做事实检查,看看在这里找到解决方案的正确理由是什么?

5 个答案:

答案 0 :(得分:2)

我建议不要维护一个需要经常更新的计算列,而是在应用程序需要时根据需要计算查询中的订单总数。您可以使用如下的查询,该查询应该运行得相当快:

SELECT t1.ID, t2.OrderTotalPrice
FROM Order t1
INNER JOIN
(
    SELECT OrderID, SUM(TotalItemPrice) AS OrderTotalPrice
    FROM OrderItems
    GROUP BY OrderID
) t2
    ON t1.ID = t2.OrderID

这避免了必须维护计算列的问题,这使得管理数据库变得更加容易。针对计算列的一个强有力的论据是,它并没有真正保存数据库的任何工作。相反,总是需要维护,而按需计算列只需要在实际需要时完成。

答案 1 :(得分:1)

订单不会包含数百万个头寸,因此速度不应该是您必须担心的问题。

您的表格OrderItems包含QuantityUnitPrice以及TotalItemPrice。这似乎已经多余了。 TotalItemPrice始终不是Quantity x UnitPrice吗?如果UnitPrice已经是支付的总价格(而不是必须添加增值税才能获得TotalItemPrice)的净价格。如果已经包含任何物品折扣。例如,如果有另一列item_discount_percent,我们可能会得到一个数字太多的结果,例如105.987002。在此示例中,订单是否包含105.98或105.99?我们可能希望将该值存储在TotalItemPrice中,以便明确这一点。 (并确保新的软件版本仍会打印完全相同的订单。)因此,只有在某些计算可能导致价格超过两位小数的情况下才能使用此列。

关于你的问题和TotalOrderPrice我们可以应用相同的想法:如果价格只是所有订单TotalItemPrice的总和,那么就不要存储它。如果要进行一些计算导致过多的小数位(例如order_discount_percent),您应该存储该值(舍入/截断)值。

答案 2 :(得分:1)

我会考虑数据的访问模式是什么,因为这决定了相关的利弊。

您需要多久一次:

  1. 在总订单金额上添加谓词(计算密集型除非存储总额)。
  2. 按总订单金额排序(计算密集型除非存储总额)。
  3. 修改总订单金额(计算密集型,可能是错误原因,如果存储总额)。
  4. 如果订单在创建后永远不会被修改,并且您经常将谓词放在总计或订单上,那么我有信心将总数存储在订单表中。

    如果订单经常被修改,但您很少需要通过它来对总数或订单放置谓词,那么我有信心不存储总数。

    对您而言,正确的方法在很大程度上取决于这两个极端之间的平衡点,以及您愿意采用不良性能不正确数据的风险。

答案 3 :(得分:1)

这是我的第二个答案,与我的第一个答案截然不同......

通常可以避免数据库中的冗余,因为它可能导致不一致。例如,如果您看到存储的订单TotalOrderPrice与您从头寸计算的订单不匹配,您会怎么做?为了避免这种不一致,我们避免了裁员。

但是,在数据仓库中,您会邀请冗余以便更快地访问数据。这意味着您可以拥有一个包含纯OrderOrderItems表的订单系统,并且拥有一个数据仓库系统,该系统会按时间间隔进行更新,并且有一个Order表,其中包含{{1 }}

进一步思考......您的系统中的订单是否会发生变化?如果不是为什么不存储您打印的内容,即冗余地存储TotalOrderPrice。 (您可以使用一些数据库机制来防止部分删除或更新订单以使其更安全。)如果稍后TotalOrderPrice确实与您从位置计算的内容不匹配,那么这甚至表示您编写订单时软件出现问题。因此,存储TotalOrderPrice是一个优势,突然可能会让我们检测这些错误,并在会计中进行更正。

这样说:通常订单会被写入,之后不会更改。由于不会应用任何更改,您可以轻松地将TotalOrderPrice存储在订单表中,并且具有以下优点:稍后查看您发送/打印的订单价格并更快地检索价格。

答案 4 :(得分:1)

一般来说,我认为你应该避免违反规范化规则,直到需要为止。这意味着避免数据冗余,以避免更新异常,并动态计算事物。我已经看到很多可怕的数据库被创建,因为开发人员担心有一天数据库可能无法应对应用程序负载;事实上,在设计良好,索引良好且维护良好的数据库中,这种情况很少见。如果正确设计和维护数据库,RDBMS是处理事务系统中大量规范化数据的非常好的工具。

这并不意味着您需要在应用程序逻辑中进行计算 - 实际上我会避免这种情况。相反,做一个视图(看起来像Tim Biegeleisen在他的答案中建议的那样)进行计算。如果在未来的某个时候您发现这不能很好地扩展,您可以更改表和视图,以及填充此表的任何内容 - 如果需要进行此更改,这可以最大限度地减少对应用程序的干扰。如果通过存储过程填充表,那么您可能根本不需要对前端应用程序逻辑进行任何更改,以便从即时计算切换到预先计算。