5

Giả sử tôi có các bảng sau:SQL: Bình thường hoá cơ sở dữ liệu trong khi vẫn giữ chế

 ____________________    ____________________ 
    |  Organisms  |   |  Species  | 
    |--------------------|   |--------------------| 
    |OrganismId (int, PK)|   |SpeciesId (int, PK) | 
    |SpeciesId (int, FK) |∞---------1|Name (varchar)  | 
    |Name (varchar)  |   |____________________| 
    |____________________|      1 
       1         | 
       |         | 
       |         | 
       ∞         ∞ 
    ______________________  ____________________   _______________ 
    | OrganismPropsValues |  | SpeciesProps  |  |  Props  | 
    |----------------------|  |--------------------|  |---------------| 
    |OrganismId (int, FK) |  |PropId (int,PK,FK) | ∞-----1|PropId (int,PK)| 
    |PropId (int, FK)  |  |SpeciesId(int,PK,FK)|  |Name (varchar) | 
    |Value (varchar)  |  |____________________|  |_______________| 
    |______________________|            1 
       ∞               | 
       |               | 
       ----------------------------------------------------------- 

Một lời giải thích nhanh chóng về những gì tôi đang cố gắng để thể hiện đây: giả sử chúng ta có một danh sách các loài, chẳng hạn như mèo, chó Chúng tôi cũng có một tập hợp các thuộc tính (đạo cụ viết tắt để tôi có thể phù hợp với nó dễ dàng hơn trong sơ đồ) áp dụng cho một số nhưng không nhất thiết phải tất cả các loài - ví dụ, có thể là đuôi dài (đối với loài có đuôi), màu mắt (đối với những người có mắt), v.v.

Loài là một bảng liên kết xác định thuộc tính nào áp dụng cho loài nào - vì vậy ở đây chúng tôi sẽ ld có {Con người, Màu mắt}, {Chó, Màu mắt}, {Mèo, Màu mắt}, {Chó, Chiều dài đuôi}, {Mèo, Chiều dài đuôi}. Chúng tôi không có {Human, Tail Length} vì Tail Length rõ ràng không phải là tài sản hợp lệ để áp dụng cho con người.

Bảng sinh vật thực sự "triển khai" của các loài - Vì vậy, ở đây chúng tôi có thể có {Human, Bob}, {Dog, Rufus} và {Cat, Felix}.

Đây là vấn đề của tôi: trong bảng OrganismPropsValues, tôi muốn lưu trữ 'giá trị' của thuộc tính cho từng sinh vật - ví dụ, đối với Bob, tôi muốn lưu trữ {Bob, Eye Color, Blue}. Đối với Rufus, tôi muốn lưu trữ {Rufus, Eye Color, Brown} và {Rufus, Tail Length, 20} (tương tự cho Felix). Tuy nhiên, vấn đề của tôi là trong lược đồ mà tôi đã trình bày chi tiết, có thể lưu trữ {Bob, Tail Length, 10}, mặc dù tuple {Human, Tail Length} không tồn tại trong SpeciesProps. Làm thế nào tôi có thể sửa đổi lược đồ này để tôi có thể thực thi các ràng buộc được xác định trong SpeciesProps trong OrganismPropsValues, trong khi duy trì chuẩn hóa đầy đủ?

+0

Tùy thuộc vào DB (ví dụ Oracle) Tôi sẽ tạo một số thủ tục lưu trữ cho INSERT/UPDATE/DELETE và thực hiện bất kỳ ràng buộc phức tạp nào trong đó ... – Yahia

+0

@Yahia cảm ơn đề xuất, nhưng nếu có cách nào để làm điều này mà không cần giới thiệu quy trình, trình kích hoạt, v.v. Tôi thích điều đó. Đây là MS-SQL (2008). – Andrew

+0

Điều đó khiến tôi đau đầu.Điều này sẽ rất khủng khiếp khi truy vấn (nghĩ có bao nhiêu người tham gia sẽ nhận được tất cả dữ liệu về con người!) Và là một thiết kế tồi tệ đến nỗi tôi không biết bắt đầu từ đâu. Cơ sở dữ liệu không phải là Đối tượng và không được thiết kế như đối tượng. Các bảng EAV là một giải pháp cực kỳ kém. Thuê một nhà thiết kế cơ sở dữ liệu thực sự. – HLGEM

Trả lời

4

Bạn đang triển khai Entity-Attribute-Value antipattern. Đây không thể là một thiết kế cơ sở dữ liệu chuẩn hóa, vì nó không quan hệ.

Những gì tôi sẽ đề nghị thay vào đó là mô hình Class Table Inheritance thiết kế:

  • Tạo một bảng cho sinh vật, chứa các thuộc tính chung cho tất cả các loài.
  • Tạo một bảng cho mỗi loài, có chứa các thuộc tính cụ thể cho loài đó. Mỗi bảng trong số này có mối quan hệ 1-1 với các Sinh vật, nhưng mỗi thuộc tính đều nằm trong cột riêng của nó.

    ____________________    ____________________ 
    |  Organisms  |   |  Species  | 
    |--------------------|   |--------------------| 
    |OrganismId (int, PK)|   |SpeciesId (int, PK) | 
    |SpeciesId (int, FK) |∞---------1|Name (varchar)  | 
    |Name (varchar)  |   |____________________| 
    |____________________| 
          1 
          | 
          | 
          1 
    ______________________ 
    | HumanOrganism  | 
    |----------------------| 
    |OrganismId (int, FK) | 
    |Sex  (enum)  | 
    |Race  (int, FK) | 
    |EyeColor (int, FK) | 
    |....     | 
    |______________________| 
    

này có nghĩa là bạn sẽ tạo ra nhiều bảng, nhưng xem xét việc này như một sự cân bằng với nhiều lợi ích thiết thực để lưu trữ tài sản một cách relationally đúng:

  • Bạn có thể sử dụng dữ liệu SQL các loại phù hợp, thay vì xử lý mọi thứ một dạng vec-tơ tự do.
  • Bạn có thể sử dụng các ràng buộc hoặc bảng tra cứu để hạn chế các thuộc tính nhất định bằng một tập hợp giá trị được xác định trước.
  • Bạn có thể đặt các thuộc tính bắt buộc (tức là NOT NULL) hoặc sử dụng các ràng buộc khác.
  • Dữ liệu và chỉ mục được lưu trữ hiệu quả hơn.
  • Truy vấn dễ dàng hơn cho bạn để viết và dễ dàng hơn cho RDBMS thực thi.

Để biết thêm về thiết kế này, xem Martin Fowler của cuốn sách Patterns of Enterprise Application Architecture, hoặc trình bày của tôi Practical Object-Oriented Models in SQL, hoặc cuốn sách của tôi, SQL Antipatterns: Avoiding the Pitfalls of Database Programming.

+0

Cảm ơn đề xuất thay thế ... Tôi sẽ xem xét mẫu này. – Andrew

2

Hmm ...
Dưới đây là một cách để thực hiện:
Thêm SpeciesPropsId vào bảng SpecropProps.
Thay thế PropId bằng SpeciesPropsId trong bảng OrganismPropsBalues.
Bạn sẽ cần thay đổi các ràng buộc một chút.
Cần thêm SpeciesProps vào OrganismPropsValues ​​constrain.
Cần phải loại bỏ OrganismPropsValues ​​to Props constrain.

Về mặt kỹ thuật, bạn không phải loại bỏ PropId khỏi OrganismPropsValues, nhưng nếu bạn giữ nó, nó sẽ làm cho dữ liệu được làm lại.

1

Một cách khác để đạt được các ràng buộc này là thay đổi bảng PK của Organism bằng cách bỏ OrganismId và thêm No. Sau đó, tạo PK hợp chất (SpeciesId, No). Vì vậy, "Bob" sẽ (Human, 1), "Rufus" sẽ (Dog, 1) vv

Sau đó, thêm trong bảng OrganismPropsValues, các SpeciesIdNo (loại bỏ các OrganismId.)

này sẽ cho phép thay đổi FK từ OrganismPropsValues để Props để tham khảo SpeciesProps thay vì:

 ____________________    ____________________ 
    |  Organisms  |   |  Species  | 
    |--------------------|   |--------------------| 
    |SpeciesId (int, FK) |   |SpeciesId (int, PK) | 
    |No (int)   |∞---------1|Name (varchar)  | 
    |Name (varchar)  |   |____________________| 
    |PK (SpeciedId,No) |      1 
    |____________________|      | 
       1         | 
       |         | 
       |         | 
       ∞         ∞ 
    ______________________  ____________________   _______________ 
    | OrganismPropsValues |  | SpeciesProps  |  |  Props  | 
    |----------------------|  |--------------------|  |---------------| 
    |SpeciesId (int, PK) |  |PropId (int,PK,FK) | ∞-----1|PropId (int,PK)| 
    |No (int, PK)   |  |SpeciesId(int,PK,FK)|  |Name (varchar) | 
    |PropId (int, PK)  |  |____________________|  |_______________| 
    |Value (varchar)  |     1 
    |FK (SpeciesId,No)  |     | 
    |FK (SpeciesId,PropId) |     | 
    |______________________|     | 
       ∞        | 
       |        | 
       ------------------------------- 
+0

@HLGEM với vấn đề "EAV" là trường 'OrganismPropsValues.Value'. Không có cách nào dễ dàng để kiểm tra tính toàn vẹn trên trường đó vì nó có thể lưu trữ các loại dữ liệu khác nhau. Ví dụ, bạn tránh lưu trữ '{Bob, Tail Length, 10}' với cấu trúc db này nhưng bạn không thể tránh '{Rufus, Tail Length, Blue}' hoặc '{Bob, Eye Color, 20}'. –

+0

Thanks-- Tôi đã nghe những điều cơ bản về EAV trước đây, nhưng chưa bao giờ sử dụng một trong thực tế. Đây thực sự là một dự án phụ mà tôi đang thực hiện - không phải là tôi sắp đưa nó vào phần mềm sản xuất - nhưng dữ liệu tôi đang cố mô hình ở đây dường như kêu gọi nó. Tất nhiên bất kỳ đề xuất cho các lựa chọn thay thế được chào đón - Tôi không được thiết lập khi sử dụng nó. – Andrew

2

Bất cứ khi nào bạn có phụ thuộc hình kim cương như thế này, hãy cân nhắc chú trọng hơn vào tổng hợp KEY CHÍNH.

Cụ thể, xác định Sinh vật không chỉ bởi OrganismId, nhưng bởi sự kết hợp của SpeciesIdOrganismSubId (bạn vẫn có thể có OrganismId, nhưng giữ nó như một chìa khóa thay thế - không hiển thị ở đây cho ngắn gọn).

Khi bạn làm điều đó, mô hình của bạn có thể được thực hiện để trông như thế này:

ER Model

Điều quan trọng cần lưu ý ở đây là SpeciesId là "tuyên truyền" xuống cả cạnh của kim cương này hình đồ thị. Đây là những gì mang lại cho bạn những hạn chế mong muốn của việc không thể "gán một giá trị" cho một tài sản không được "khai báo" cho các loài đã cho.

BTW, sử dụng số ít khi đặt tên cho bảng của bạn. Ngoài ra, hãy xem xét sử dụng các khóa chính tự nhiên (ví dụ: SpeciesName thay vì SpeciesId làm PK) - nếu được thực hiện, nó có thể làm tăng đáng kể tốc độ JOIN của bạn (đặc biệt là kết hợp với phân cụm).

Các vấn đề liên quan