Có lãng phí khi tạo một bảng cơ sở dữ liệu mới thay vì sử dụng kiểu dữ liệu enum không?


38

Giả sử tôi có 4 loại dịch vụ tôi cung cấp (chúng không có khả năng thay đổi thường xuyên):

  • Kiểm tra
  • Thiết kế
  • Lập trình
  • Khác

Giả sử tôi có 60-80 dịch vụ thực tế, mỗi dịch vụ thuộc một trong các loại trên. Ví dụ: 'một dịch vụ' có thể là "Chương trình thử nghiệm sử dụng kỹ thuật A" và nó thuộc loại "Thử nghiệm".

Tôi muốn mã hóa chúng vào cơ sở dữ liệu. Tôi đã đưa ra một vài lựa chọn:

Tùy chọn 0:

Sử dụng VARCHARtrực tiếp để mã hóa loại dịch vụ trực tiếp dưới dạng chuỗi

Lựa chọn 1:

Sử dụng cơ sở dữ liệu enum. Nhưng, enum là ác

Lựa chọn 2:

sử dụng hai bảng:

service_line_item (id, service_type_id INT, description VARCHAR);
service_type (id, service_type VARCHAR);

Tôi thậm chí có thể tận hưởng tính toàn vẹn tham chiếu:

ALTER service_line_item 
    ADD FOREIGN KEY (service_type_id) REFERENCES service_type (id);

Nghe có vẻ hay đấy chứ?

Nhưng tôi vẫn phải mã hóa mọi thứ và xử lý các số nguyên, tức là khi điền vào bảng. Hoặc tôi phải tạo lập trình phức tạp hoặc các cấu trúc DB khi điền hoặc xử lý bảng. Cụ thể, THAM GIA khi trực tiếp xử lý cơ sở dữ liệu hoặc tạo các thực thể hướng đối tượng mới ở phía lập trình và đảm bảo tôi vận hành chúng chính xác.

Tùy chọn 3:

Không sử dụng enum, không sử dụng hai bảng, mà chỉ sử dụng một cột số nguyên

service_line_item (
    id,
    service_type INT,        -- use 0, 1, 2, 3 (for service types)
    description VARCHAR
);

Điều này giống như một 'enum giả' đòi hỏi nhiều chi phí hơn về mặt mã của mọi thứ, như nghĩa là biết điều đó {2 == 'Programming'}và xử lý nó một cách thích hợp.

Câu hỏi:

Hiện tại tôi đã triển khai nó bằng cách sử dụng Tùy chọn 2 , được hướng dẫn theo các khái niệm

  1. không sử dụng enum (tùy chọn 1)
  2. tránh sử dụng cơ sở dữ liệu dưới dạng bảng tính (tùy chọn 0)

Nhưng tôi không thể cảm thấy điều đó có vẻ lãng phí đối với tôi về mặt lập trình và chi phí nhận thức - tôi phải nhận thức được hai bảng và đối phó với hai bảng, so với một bảng.

Đối với một "cách ít lãng phí", tôi đang xem xét Option 3. CNTT nhẹ hơn và về cơ bản đòi hỏi các cấu trúc mã giống nhau để hoạt động (với các sửa đổi nhỏ nhưng độ phức tạp và cấu trúc về cơ bản là giống nhau nhưng với một bảng duy nhất)

Tôi cho rằng lý tưởng không phải lúc nào cũng lãng phí, và có những trường hợp tốt cho một trong hai lựa chọn, nhưng liệu có một hướng dẫn tốt về việc khi nào nên sử dụng Tùy chọn 2 và khi Tùy chọn 3 không?

Khi chỉ có hai loại (nhị phân)

Để thêm một chút cho câu hỏi này ... trong cùng một địa điểm, tôi có tùy chọn nhị phân của Dịch vụ "Tiêu chuẩn" hoặc "Ngoại lệ", có thể áp dụng cho chi tiết đơn hàng dịch vụ. Tôi đã mã hóa bằng cách sử dụng Tùy chọn 3 .

Tôi đã chọn không tạo bảng mới chỉ để giữ các giá trị {"Tiêu chuẩn", "Ngoại lệ"}. Vì vậy, cột của tôi chỉ giữ {0, 1} và tên cột của tôi được gọi exceptionvà mã của tôi đang thực hiện một bản dịch từ {0, 1} => {STANDARD, EXCEPTION}(mà tôi đã mã hóa thành hằng số trong ngôn ngữ lập trình)

Cho đến nay cũng không thích cách đó ..... (không thích tùy chọn 2 cũng không phải tùy chọn 3). Tôi tìm thấy tùy chọn 2 vượt trội hơn 3, nhưng với nhiều chi phí hơn, và tôi vẫn không thể thoát khỏi những thứ được mã hóa dưới dạng số nguyên cho dù tôi sử dụng tùy chọn nào trong số 2 và 3.

ORM

Để thêm một số ngữ cảnh, sau khi đọc câu trả lời - Tôi mới bắt đầu sử dụng lại ORM (gần đây), trong trường hợp của tôi Tài liệu 2. Sau khi xác định lược đồ DB thông qua Chú thích, tôi muốn điền vào cơ sở dữ liệu. Vì toàn bộ tập dữ liệu của tôi tương đối nhỏ, tôi muốn thử sử dụng các cấu trúc lập trình để xem nó hoạt động như thế nào.

Lần đầu tiên tôi điền service_types, và sau đó service_line_itemlà s, vì đã có một danh sách từ bảng tính thực tế. Vì vậy, những thứ như 'tiêu chuẩn / ngoại lệ' và 'Kiểm tra' đều là các chuỗi trên bảng tính và chúng phải được mã hóa thành các loại thích hợp trước khi lưu trữ chúng trong DB.

Tôi tìm thấy câu trả lời SO này: Bạn sử dụng gì thay vì ENUM trong doctrine2? , đã đề xuất không sử dụng cấu trúc enum của DB, mà sử dụng một INTtrường và mã hóa các kiểu sử dụng cấu trúc 'const' của ngôn ngữ lập trình.

Nhưng như đã chỉ ra trong câu hỏi SO ở trên, tôi có thể tránh sử dụng trực tiếp các số nguyên và sử dụng các cấu trúc ngôn ngữ - hằng số - một khi chúng được định nghĩa ....

Nhưng vẫn .... cho dù bạn xoay nó như thế nào, nếu tôi bắt đầu với stringtư cách là một loại, trước tiên tôi phải chuyển đổi nó thành một loại thích hợp, ngay cả khi sử dụng ORM.

Vì vậy, nếu nói $str = 'Testing';, tôi vẫn cần phải có một khối ở đâu đó thực hiện một cái gì đó như:

switch($str):
{ 
    case 'Testing':  $type = MyEntity::TESTING; break;
    case 'Other':    $type = MyEntity::OTHER; break;
}

Điều tốt là bạn không xử lý số nguyên / số ma thuật [thay vào đó, xử lý số lượng không đổi được mã hóa], nhưng điều tệ là bạn không thể tự động kéo mọi thứ vào và ra khỏi cơ sở dữ liệu mà không cần bước chuyển đổi này hiểu biết.

Và đó là điều tôi muốn nói, một phần, bằng cách nói những điều như "vẫn phải mã hóa mọi thứ và xử lý số nguyên". (Được cấp, bây giờ, sau nhận xét của Ocramius, tôi sẽ không phải giao dịch trực tiếp với số nguyên, nhưng xử lý các hằng số được đặt tên và một số chuyển đổi sang / từ các hằng số, nếu cần).


9
Dù bạn làm gì, đừng làm # 3. Kẻ thái nhân cách duy trì nó sẽ liên tục phải tìm ra ý nghĩa của những con số ma thuật đó. Nếu bạn làm điều đó, tốt hơn là bạn hy vọng họ không biết bạn sống ở đâu. blog.codinghorror.com/coding-for-violent-psychopaths
RubberDuck

7
Tôi thích Tùy chọn 2. Nếu bạn không thích sự phổ biến của các bảng tra cứu, hãy sử dụng một bảng và thêm cột "loại tra cứu". Nhưng vâng, tạo bảng tra cứu là cách "chuẩn" để thực hiện việc này, vì nó cho phép bạn thực hiện những điều thú vị như dễ dàng tạo ra một danh sách thả xuống trong giao diện người dùng.
Robert Harvey

Đừng sử dụng "EDIT" trong bài viết của bạn ở đây; chúng tôi không phải là một diễn đàn. Mỗi bài đăng Stack Exchange đã chứa một lịch sử chỉnh sửa chi tiết mà bất cứ ai cũng có thể xem.
Robert Harvey

Nếu tôi không thể sử dụng EDIT, tôi sẽ sử dụng cái gì?
Dennis

Chỉ cần chỉnh sửa bài đăng và làm cho nó trông tự nhiên, như tôi đã làm. Xem lịch sử chỉnh sửa để xem xét các thay đổi.
Robert Harvey

Câu trả lời:


35

Tùy chọn # 2, sử dụng các bảng tham chiếu, là cách làm tiêu chuẩn. Nó đã được sử dụng bởi hàng triệu lập trình viên, và được biết là có tác dụng. Đó là một mô hình , vì vậy bất cứ ai khác nhìn vào công cụ của bạn sẽ ngay lập tức biết những gì đang xảy ra. Có các thư viện và công cụ hoạt động trên cơ sở dữ liệu, giúp bạn tiết kiệm rất nhiều công việc, sẽ xử lý chính xác. Những lợi ích của việc sử dụng nó là vô số.

Có lãng phí không? Có, nhưng chỉ một chút. Bất kỳ cơ sở dữ liệu nửa nào cũng sẽ luôn giữ các bảng nhỏ được nối thường xuyên như vậy, do đó, sự lãng phí thường không thể chấp nhận được.

Tất cả các tùy chọn khác mà bạn mô tả là ad hoc và hacky, bao gồm cả MySQL enum, vì nó không phải là một phần của tiêu chuẩn SQL. (Ngoài ra, điều hấp dẫn enumlà việc triển khai của MySQL, chứ không phải ý tưởng. Tôi sẽ không phiền khi thấy nó một ngày là một phần của tiêu chuẩn.)

Tùy chọn cuối cùng số 3 của bạn với việc sử dụng số nguyên đơn giản là đặc biệt khó khăn . Bạn nhận được điều tồi tệ nhất trong tất cả các thế giới: không toàn vẹn tham chiếu, không có giá trị được đặt tên, không có kiến ​​thức dứt khoát trong cơ sở dữ liệu về giá trị đại diện cho cái gì, chỉ là các số nguyên tùy ý ném khắp nơi. Bằng mã thông báo này, bạn cũng có thể thoát khỏi việc sử dụng các hằng số trong mã của mình và bắt đầu sử dụng các giá trị được mã hóa cứng thay thế. circumference = radius * 6.28318530718;. Thế còn cái đó?

Tôi nghĩ bạn nên kiểm tra lại lý do tại sao bạn tìm thấy các bảng tham chiếu đầy đủ. Không ai khác tìm thấy chúng một cách tàn nhẫn, theo như tôi biết. Có thể đó là vì bạn không sử dụng đúng công cụ cho công việc?

Câu của bạn về việc phải "mã hóa mọi thứ và xử lý các số nguyên" hoặc phải "tạo các cấu trúc lập trình phức tạp" hoặc "tạo các thực thể hướng đối tượng mới ở phía lập trình", cho tôi biết rằng có lẽ bạn đang cố gắng thực hiện quan hệ đối tượng ánh xạ (ORM) khi đang phân tán trong toàn bộ mã ứng dụng của bạn hoặc trong trường hợp tốt nhất bạn có thể đang cố gắng cuộn cơ chế ánh xạ quan hệ đối tượng của riêng mình, thay vì sử dụng công cụ ORM hiện có cho công việc, như Hibernate. Tất cả những điều này là một làn gió với Hibernate. Phải mất một chút thời gian để tìm hiểu nó, nhưng một khi bạn đã học nó, bạn thực sự có thể tập trung vào phát triển ứng dụng của mình và quên đi cơ chế khó chịu của nitty về cách trình bày công cụ trên cơ sở dữ liệu.

Cuối cùng, nếu bạn muốn làm cho cuộc sống của mình dễ dàng hơn khi làm việc trực tiếp với cơ sở dữ liệu, có ít nhất hai điều bạn có thể làm, mà tôi có thể nghĩ ra ngay bây giờ:

  1. Tạo các khung nhìn nối các bảng chính của bạn với bất kỳ bảng tham chiếu nào mà chúng tham chiếu, sao cho mỗi hàng không chỉ chứa các id tham chiếu mà còn cả các tên tương ứng.

  2. Thay vì sử dụng id số nguyên cho bảng tham chiếu, hãy sử dụng cột CHAR (4), với chữ viết tắt 4 chữ cái. Vì vậy, id của danh mục của bạn sẽ trở thành "KIỂM TRA", "DSGN", "PROG", "OTHR". (Tất nhiên, mô tả của họ sẽ vẫn là những từ tiếng Anh thích hợp.) Nó sẽ chậm hơn một chút, nhưng tin tôi đi, không ai để ý đâu.

Cuối cùng, khi chỉ có hai loại, hầu hết mọi người chỉ sử dụng một cột boolean. Vì vậy, cột "tiêu chuẩn / ngoại lệ" đó sẽ được triển khai dưới dạng boolean và nó sẽ được gọi là "IsException".


3
Ngoài ra, Postgres cũng có các loại enum . Chúng đơn giản và không có gì đặc biệt, cho phép bạn sử dụng một chuỗi có thể đọc được làm giá trị, nhưng có một số nguyên hiệu quả hơn được sử dụng dưới mui xe.
Kat

Điều gì về trường hợp khi dữ liệu được lặp đi lặp lại, nhưng không dư thừa (ví dụ: sẽ không dẫn đến dị thường cập nhật / chèn / xóa)? Ví dụ: giới tính của một người (không có khả năng giới thiệu các loại dữ liệu mới, sẽ không bao giờ cần thay đổi tên của giới tính, v.v.)
Adam Thompson

Điều này: bởi vì cuối cùng bạn sẽ phát hiện ra bạn cần một "môi trường chấp nhận" và enum không thay đổi của bạn cần phải được thay đổi.
Pieter B

3

Tùy chọn 2 với hằng số hoặc enum ở cuối chương trình.
Mặc dù nó trùng lặp kiến ​​thức, vi phạm nguyên tắc Nguồn đơn Sự thật, bạn có thể đối phó với nó bằng cách sử dụng kỹ thuật Fail-fast . Khi hệ thống của bạn tải, nó sẽ kiểm tra xem các giá trị enum hay const tồn tại trong cơ sở dữ liệu. Nếu không, hệ thống sẽ đưa ra một lỗi và từ chối tải. Nhìn chung sẽ rẻ hơn khi sửa lỗi này vào thời điểm này so với sau này khi điều gì đó nghiêm trọng hơn có thể xảy ra.


0

Không có gì ngăn bạn sử dụng các chuỗi [ngắn] làm khóa, vì vậy bạn vẫn có thể đọc được tên trong bảng của mình và không dùng đến mã hóa số thay thế vô nghĩa. Bạn vẫn nên có một bảng riêng để mô tả các loại dịch vụ, nếu không có khả năng, giả sử, ứng dụng của bạn đi quốc tế!

Người dùng của bạn có thể thấy bốn danh mục của bạn bằng ngôn ngữ của họ , nhưng các bảng cơ sở dữ liệu của bạn vẫn chứa các giá trị mà bạn có thể đọc - và không có danh mục nào yêu cầu bất kỳ thay đổi cấu trúc cơ sở dữ liệu hoặc mã nào!

table service_type 
( id VARCHAR 
, name VARCHAR 
  primary key ( id ) 
);
table service_line_item 
( id 
, service_type VARCHAR 
, description VARCHAR
  foreign key ( service_type ) references service_type ( id )
);

select * from service_type ; 

+-------------+----------------+
| id          | name           |
+-------------+----------------+
| Testing     | Testen         |
| Design      | Design         | 
| Programming | Programmierung |
| Other       | Andere         |
+-------------+----------------+

hoặc, cho khách hàng Pháp của bạn ...

update services_types set name = 'Essai'         where id = 'Testing'; 
update services_types set name = 'Conception'    where id = 'Design'; 
update services_types set name = 'Programmation' where id = 'Programming'; 
update services_types set name = 'Autre'         where id = 'Other'; 
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.