2010-02-02 130 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

我經常贊成計算字段假設你這樣做是正確的在定義字段數據庫計算。這樣無論數據如何變化,計算總是適用的。我只會這樣做,但如果您需要在包含多條記錄的報告中獲得這些計算結果。當然,在查詢中編寫公式很容易,但如果頻繁計算此數字,則會浪費服務器資源(計算字段只在信息更改時執行計算),並且如果必須執行數百萬的計算,可能會嚴重降低查詢速度的報告記錄。物化視圖也是一個好主意(因爲它會預先計算),但常規視圖會讓您無法多次編寫計算,它不具有計算字段的性能優勢。另一方面,如果我不需要(即,我可以用其他方式解決問題),我就不會創建視圖,因爲當人們開始在視圖之上創建視圖時,它們可能會使您陷入真正的性能問題。當螺絲刀是你需要的時候不要使用錘子。

如果使用得當,計算字段是功能強大的工具,數據庫設計人員經常會忽略它。