Bộ phận thiết kế DB


8

Tôi đang phát triển một công cụ xử lý các bộ phận (điện). Các phần có thể được tạo, xem, sửa đổi, xóa, nhóm và vv ...

Để làm cho câu hỏi này hữu ích cho khách truy cập trong tương lai, tôi muốn giữ câu hỏi này phổ biến vì việc quản lý các bộ phận trong DB là rất phổ biến cho dù bộ phận nào trong DB (CD, xe hơi, thực phẩm, sinh viên, ...).

Tôi đang nghĩ về 3 thiết kế DB khác nhau:

  1. Sử dụng một bảng phần và các bảng dẫn xuất cho các thuộc tính phần chuyên biệt.

    Parts      (id, part_type_id, name)
    PartTypes  (id, name)
    Wires      (id, part_id, lenght, diameter, material)
    Contacts   (id, part_id, description, picture)
    
  2. Chỉ sử dụng bảng phần chuyên ngành.

    Wires      (id, name, lenght, diameter, material)
    Contacts   (id, name, description, picture)
    
  3. Sử dụng bảng Part-, PartTypes-, ValueTypes- và PartValues ​​có chứa tất cả các giá trị.

    PartTypes  (id, name)
    ValueTypes (id, part_type_id, name)
    Parts      (id, part_type_id, name)
    PartValues (part_id, value_type_id, value)
    

Cái nào thích hơn và tại sao? Hoặc có một cái tốt hơn?
Tôi quan tâm đến các truy vấn DB. Tôi không muốn các truy vấn trở nên quá chậm hoặc phức tạp.

Cập nhật

Số lượng các loại trong DB được đưa ra khá nhiều và tĩnh vì chúng dựa trên tiêu chuẩn quốc tế và sẽ được tăng cường hiếm khi.


Đây có phải là về SQL DB hoàn toàn (hoàn toàn là quan hệ) hay NOSQL DB cũng là một lựa chọn không?
c-smile

@ c-smile: Vì tôi chưa làm việc với NOSQL, tôi thực sự không biết liệu đó có phải là một lựa chọn không. Tôi cởi mở với mọi thứ.
juergen d

Câu trả lời:


16

Tùy chọn 3 : (đôi khi)
Tùy chọn 3 là thiết kế "EAV" . Về lý thuyết, nó là tốt vì các trường được lấy ra khỏi cấu trúc bảng và trở thành dữ liệu. Nhưng nó cho hiệu suất khủng khiếp. Nó cũng không cho phép sử dụng lập chỉ mục thích hợp. Và nó làm cho các truy vấn phức tạp hơn nhiều.

Tôi sẽ chỉ sử dụng EAV trong trường hợp đặc biệt. Tôi đã sử dụng EAV để tính toán các bộ phận phụ trợ cần thiết cho các đơn đặt hàng và nó hoạt động tốt. Nhưng hãy rất mệt mỏi khi sử dụng nó làm thiết kế cho các bảng cốt lõi của bạn.

Tùy chọn 2 : (không bao giờ?)
Tùy chọn 2 là không. Còn các lĩnh vực được chia sẻ thì sao? Bạn sẽ nhân đôi cấu trúc bảng cho mọi trường chia sẻ? Nó sẽ yêu cầu bạn bao gồm các công đoàn trong các báo cáo của toàn bộ hệ thống.

Cách 1 : (người chiến thắng!)
Tùy chọn 1 có vẻ hơi quá cơ bản nhưng có lẽ đây là cách đặt cược tốt nhất cho các bảng cốt lõi của bạn. Tất cả các phần sử dụng cùng một bảng chính cho các trường được chia sẻ để nó tránh các kết hợp trong báo cáo của bạn. Nó có hiệu suất tuyệt vời cho phép sử dụng lập chỉ mục thích hợp. Truy vấn theo phong cách truyền thống và đơn giản.

Nhược điểm của tùy chọn 1 là bạn không thể thêm các trường động. Nhưng bạn có thực sự muốn? Bằng cách tự động thêm các trường bạn đang thực hiện thiết kế cơ sở dữ liệu vào thời gian chạy.


+1, nhưng hãy xem câu trả lời của tôi để xem lý do đằng sau tùy chọn # 2 có thể là gì.
Doc Brown

Sau một số suy nghĩ và dựa trên các lưu ý từ OP rằng các bộ phận là tiêu chuẩn cố định tuyệt đối theo quy định, tôi đồng ý với Tùy chọn # 1 và +1 cho câu trả lời tốt, mặc dù anh ấy chắc chắn nên nhớ Tùy chọn # 3 có thể là một di chuyển điểm trong tương lai, cũng rất quan trọng vì không ai khác đề cập đến nó: Các liên kết ngoài có đặc điểm hiệu suất kém nói chung và nên tránh khi có thể Chỉ cần thêm rằng vì Tùy chọn # 1 sẽ liên quan đến các kết nối bên ngoài, nhưng trong trường hợp này vẫn có thể đáng giá như Tùy chọn # 3 có những cạm bẫy hiệu suất riêng.
Jimmy Hoffa

2
Lựa chọn 1 có vẻ quá cơ bản? Không có cách nào, đó chắc chắn là cách để làm điều đó. Jimmy là sai, nói chung tham gia bên ngoài không có đặc điểm hiệu suất kém nói chung. Miễn là bạn lập chỉ mục đúng, nó sẽ ổn thôi.
Rocklan

6

Tôi có xu hướng không lựa chọn # 3.

Tùy chọn # 3 là thiết lập cặp tên-giá trị vi phạm chuẩn hóa.

Lý tưởng nhất, một nỗ lực để có một số mức độ bình thường hóa của cơ sở dữ liệu. Phấn đấu để chuẩn hóa hoàn toàn và sau đó không chuẩn hóa khi cần thiết khi nó được xác định cho các vấn đề tùy chỉnh hoặc hiệu suất.

Hãy xem xét truy vấn "tên và bộ phận ID cho tất cả các dây được làm bằng đồng là gì"

Cấu trúc số 1 là

select
  name, parts.id
from
  wire, parts
where
  wire.material = 'copper'
  and wire.part_id = parts.id

Cấu trúc số 2 là

select id, name from wire where material = 'copper'

Cấu trúc số 3 là

select
  parts.name,
  parts.id,
from
  parts, part_types, part_values, value_types
where
  part_types.name = "wire"
  and parts.part_type_id = part_types.id
  and value_types.name = "material"
  and value_types.id = part_values.type_value_id
  and part_values.value = "copper"

Cũng xem xét sự phức tạp của chèn và xóa khỏi hệ thống.

Một số đọc thêm về lý do tại sao không # 3 - Lời nguyền của cặp giá trị tên


2
Vâng, cặp giá trị tên là xấu xa, tôi nghĩ tất cả đều đồng ý, nhưng nó vẫn tiếp tục vì đó là một điều ác cần thiết. Có lẽ # 3 là không cần thiết ở đây, nhưng nó xuất hiện rất nhiều như các cấu trúc bảng mà tôi đã thấy trở nên không thể kiểm soát được và cuối cùng cần phải không chuẩn hóa thành dạng cặp giá trị tên. Tuy nhiên, nếu nó đã được sửa thì có lẽ # 1 là cách tiếp cận phù hợp (giả sử các truy vấn sẽ muốn hành động theo các tổng hợp của các phần khác nhau, nếu không thì # 2 vẫn ổn)
Jimmy Hoffa

Ngoài ra, bạn không sử dụng các phép nối ở đây, cuối cùng đưa công việc không hợp lệ vào mệnh đề where sẽ đi vào phép nối giống như part_type_id = part_types.idvalue_types.id = part_values.type_value_idcả hai mệnh đề tham gia rời khỏi nơi mà loại phần là dây, loại giá trị là vật liệu và giá trị là đồng tương đối ngắn gọn
Jimmy Hoffa

@JimmyHoffa Tôi chỉ đang làm một phiên bản rút gọn nhanh để cho thấy nó trông như thế nào chứ không phải là sql lý tưởng. Tùy chọn thứ ba mà tôi đã thấy trong cấu trúc bảng của Redmine nơi các cặp tên / giá trị được thêm vào hệ thống một cách nhanh chóng. Phải thực hiện cập nhật cơ sở dữ liệu để thêm trường tùy chỉnh mới là không thực tế - vì vậy giá trị tên là cấu trúc phù hợp. Tuy nhiên, nó làm cho các truy vấn cơ sở dữ liệu chậm hơn một chút (các chỉ mục không hài lòng vì kiểu này trở thành chuỗi cho mọi thứ) và truy vấn hơi xấu.

1
Lần trước tôi đã thực hiện tùy chọn # 3, đó là trong MSSQL và tôi đã sử dụng loại SQL_Variant, tôi tin rằng các chỉ mục như vậy nhiều hơn một chút bởi vì nó liệt kê chúng theo loại rồi giá trị nếu tôi không nhầm, mặc dù vậy nó vẫn phức tạp hơn Cách tiếp cận và như bạn đã nói là tốt nhất khi bạn biết sẽ có sự tăng trưởng nhất quán của các loại mới, lần trước tôi đã làm điều này, đó là chuyển đổi một bảng có 60 cột; 1 cho mỗi khóa luôn tăng trưởng, vì vậy những kịch bản này rõ ràng xảy ra nhưng có lẽ đây không phải là một trong số chúng, điều đó sẽ tùy thuộc vào OP để xác định.
Jimmy Hoffa

4

Tôi đi tùy chọn 3

Tùy chọn 1 là xấu vì bạn không muốn tham gia của mình dựa trên giá trị được nộp. ( I EIf type ="Wire" join to TblWire )

Tùy chọn 2 là xấu vì bạn không có cách nào để báo cáo toàn bộ hàng tồn kho của mình


Cũng lưu ý, tùy chọn 3 có các đặc điểm bảo trì tốt nhất cho các thuộc tính phần mới, tôi đề cập đến biểu mẫu này (mặc dù tôi chắc chắn có một thuật ngữ chung giữa các DBA cho cấu trúc mà tôi bị thiếu) dưới dạng một trục chính vì nó là một trục về cấu trúc phổ biến hơn mà bạn đã nêu chi tiết trong # 1 và # 2 và thường mọi người chỉ tạo số 1 để kết thúc việc thêm bảng / cột mới cho các loại mới, do đó, họ thường phải xoay vòng đến # 3 sau khi họ đã tạo ra một mớ hỗn độn lớn họ không thể duy trì được nữa.
Jimmy Hoffa

Đối với tùy chọn 1, bạn sẽ không bao giờ cần "nếu" trên loại trước khi tham gia. Nếu nó tham gia thành công, thì đó là loại. Tham gia có thể thay thế các bộ lọc. Bạn có thể đi xa đến mức không còn lưu trữ các loại.
mike30

@mike thì sao nếu anh ấy muốn 2 loại sản phẩm? Nếu cáp nối với "Cáp", nếu các đầu nối nối với "đầu nối", nếu anh ta nối với cả hai thì anh ta chẳng được gì! Nếu anh ta rời khỏi, anh ta sẽ bị trùng lặp!
Morons

@Morons. Còn lại tham gia tổng thể với các bảng phụ. Bộ lọc trong đó calbled.ID không phải là null và Trình kết nối.ID không phải là null. Viola! Sử dụng sự thành công của tham gia như bộ lọc.
mike30

2
@Morons: lặp lại từ "cơn ác mộng" không làm cho nó đúng hơn. Nếu người ta phải sửa đổi "tất cả mã" khi một loại mới được tạo ra không liên quan gì đến "tùy chọn 1" hoặc "tùy chọn 3". Nó phải làm tốt như thế nào mã được cấu trúc. Và người ta phải sửa đổi mã ở một số nơi khi có yêu cầu mới không phải là "cơn ác mộng", đó là điều bình thường (và cũng cần thiết cho tùy chọn 3). Trước khi tranh luận thêm, tôi khuyên bạn nên thông báo cho mình về các trường hợp mẫu Thực thể-Thuộc tính-Giá trị là phù hợp và khi nào thì không . EAV đôi khi là một mô hình chống.
Doc Brown

4

Tôi sẽ bắt đầu với một mô hình dữ liệu / đối tượng cho phép kế thừa, và sau đó sử dụng ánh xạ quan hệ đối tượng tiêu chuẩn . Bằng cách này, bạn có được một lớp cơ sở Partsvà các lớp con như Wires, Contactsv.v ... Bây giờ, nếu bạn áp dụng chiến lược "ánh xạ mỗi lớp để sở hữu bảng", bạn sẽ có tùy chọn 1, đó là giải pháp "bình thường hóa" nhất và nên là chiến lược chính tắc nếu bạn không có thêm thông tin nào về các truy vấn bạn mong đợi.

Tùy chọn 2 là những gì bạn nhận được khi áp dụng phương pháp "ánh xạ từng lớp cụ thể theo bảng riêng". Điều này có thể tránh "tham gia" và có thể hoạt động tốt hơn đối với một số loại nếu các truy vấn (đặc biệt là truy vấn chỉ một "loại phần"), mặt khác, nó làm cho việc xử lý chung với tất cả các phần khó hơn và chậm hơn. Tránh điều này nếu bạn không có bất kỳ lý do đặc biệt cho nó.

Tùy chọn 3 là những gì bạn cần chỉ khi bạn muốn người dùng thay đổi số loại phần trong thời gian chạy - nếu bạn không mong đợi yêu cầu đó, tùy chọn 3 sẽ là một ví dụ hoàn hảo cho những thứ quá kỹ thuật.


2

Với cơ sở dữ liệu NOSQL DB (như MongoDB chẳng hạn), bạn sẽ chỉ cần một bộ có tên là "Bộ phận". Mỗi phần trong tập hợp đó được gọi là tài liệu - bản ghi với bộ trường biến:

{
   "_id": ObjectId("4efa8d2b7d284dea1"),
   "partType": "wire",
   "length": 102.5,
   "diameter": 1.5,
   "material": "silver"
}, 
{
   "_id": ObjectId("4efa8d2b7d284sjsq23d"),
   "partType": "contact",
   "description": "something",
   "picture": Binary(...)
}, 

Tôi nghĩ rằng đây là lưu trữ dữ liệu tự nhiên nhất cho nhiệm vụ bạn mô tả.


2

Chắc chắn đi với tùy chọn 1 nhưng với một vài sửa đổi rất đơn giản:

Parts      (id, part_type_id, name)
PartTypes  (id, name)
Wires      (id, part_id, part_type_id, lenght, diameter, material)
Contacts   (id, part_id, part_type_id, description, picture)

Sau đó, bạn có thể sử dụng các ràng buộc CHECK và các giá trị DEFAULT để đảm bảo rằng part_type_id là chính xác, và sau đó bạn có thể tham gia trên cả part_type_id và part_id. Điều này tránh việc có một phép nối có điều kiện chỉ dựa trên một bảng và nếu bạn cần thêm part_type_id vào các dây (giả sử chúng ta đang chia phần đó và thêm một bảng thuộc tính mở rộng khác) thì có thể thay đổi các ràng buộc kiểm tra và mặc định.


Bạn cũng có thể (một cách an toàn - trừ khi một số ORM yêu cầu các khóa chính một cột) loại bỏ wires.idcontacts.idvì sự (part_id, part_type_id)kết hợp sẽ đủ để xác định một phần duy nhất.
ypercubeᵀᴹ

@ypercube, chắc chắn, nhưng vì part_id là duy nhất trong trường hợp này, chỉ cần sử dụng nó làm khóa chính, với chỉ mục duy nhất thứ cấp trên part_id, part_type_id nếu bạn muốn.
Chris Travers

1

Tùy chọn 3 là chung chung hơn và có thể chứa nhiều trường hợp sử dụng hơn.

Đi tùy chọn 3, bạn có thể cần nhiều tham gia và truy vấn phức tạp hơn cho các tính năng đơn giản, trong tùy chọn 2 bạn cần các truy vấn phức tạp cho các tính năng "lớn" như kho lưu trữ và báo cáo và có thể cần sử dụng các hiệp hội để thực hiện điều đó.

Bạn luôn có thể đơn giản hóa các truy vấn của mình trong tùy chọn 3 bằng cách sử dụng Chế độ xem, nếu bạn thường chỉ cần Dây hoặc Liên hệ, hãy tạo Chế độ xem cho từng tùy chọn. Bạn có thể tối ưu hóa nó nếu nó trở nên cần thiết.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.