Mysql

最大正規化

  • February 24, 2014

我一直在研究範式規則來指導我進行數據庫設計和數據儲存,我遇到的問題是,一旦我優化了第一個範式(1NF),我想我可能會一直走下去(即使不是強制性的)。

然而,在我的設計中出現了一個我在網上或書籍中沒有遇到的例子。

一個範例概念。

我們希望儲存有關某些汽車變體的所有數據。

我們必須儲存品牌、系列、型號、開始年份、結束年份的值。

非標準化

+------+--------+-------+------------+----------+
| make | series | model | start year | end year |
+------+--------+-------+------------+----------+
| BMW  |   5    |  E12  |    1972    |   1981   |
| BMW  |   5    |  E28  |    1981    |   1988   |
| BMW  |   5    |  E34  |    1988    |   1996   |
| BMW  |   5    |  E39  |    1995    |   2004   |
+------+--------+-------+------------+----------+

正規化

car_make

+----+------+
| id | make | 
+----+------+
| 1  | BMW  | 
+----+------+

car_series

+----+--------+---------+
| id | series | make_id |
+----+--------+---------+
| 1  |   1    |    1    |
| 2  |   3    |    1    |
| 3  |   5    |    1    |
| 4  |   7    |    1    |
+----+--------+---------+

car_model

+----+-------+------------+----------+-------- --+
| id | model | start year | end year | series_id |
+----+-------+------------+----------+-----------+
| 1  |  E12  |    1972    |   1981   |     3     |
| 2  |  E28  |    1981    |   1988   |     3     | 
| 3  |  E34  |    1988    |   1996   |     3     | 
| 4  |  E39  |    1995    |   2004   |     3     |  
+----+-------+------------+----------+-----------+

最後一張表中出現了一個問題,我是否還應該在 car_model 表中包含一列 make_id ?

這是有益的,因為我不必加入 car_series,然後選擇 make_id,通過 id 選擇 make,但是我相信這可能不會堅持規範化,因為我會重複數據(即使它是一個 id 它仍然重複) .

這個設計可以進一步規範化嗎?

關於開始年和年末處理的另一個注意事項,您的專業人士將如何做到這一點?如果有任何範圍技巧來強製完整性,我很好奇(儘管 E39 重疊,讓我們說好像沒有日期重疊)。

提前感謝您的任何想法。

不,您不應該在 car_model 表中包含列 make_id,它由 series_id 隱式定義。如果您需要查看製作詳細資訊,您可以製作一個看起來像未規範化表的視圖。

不,這種設計不能進一步標準化。

要強制年份範圍不重疊,您可以添加一個觸發器(因為 MySql 不支持檢查約束)以確保start_year >= max(end_year)end_year >= start_year.

觸發器看起來像這樣:

create trigger trg_car_model_date_range_unique before insert on car_model
for each row
begin
   if new.start_year < (select max(end_year) from car_model) then
       signal sqlstate '45000' set message_text = 'Date range is not unique';
   end if;
   if new.end_year < new.start_year then
       signal sqlstate '45000' set message_text = 'Date range is invalid';
   end if;
end

引用自:https://dba.stackexchange.com/questions/59569