Dữ liệu cấu hình: bảng một hàng so với bảng cặp tên-giá trị


64

Giả sử bạn viết một ứng dụng có thể được cấu hình bởi người dùng. Để lưu trữ "dữ liệu cấu hình" này vào cơ sở dữ liệu, hai mẫu thường được sử dụng.

  1. Bảng một hàng

      CompanyName  |  StartFullScreen  |  RefreshSeconds  |  ...
    ---------------+-------------------+------------------+--------
      ACME Inc.    |        true       |       20         |  ...
    
  2. Các tên-giá trị cặp bảng

      ConfigOption   |   Value
    -----------------+-------------
     CompanyName     | ACME Inc.
     StartFullScreen | true (or 1, or Y, ...)
     RefreshSeconds  | 20
     ...             | ...
    

Tôi đã thấy cả hai lựa chọn trong tự nhiên, và cả hai đều có những ưu điểm và nhược điểm rõ ràng, ví dụ:

  • Các bảng một hàng giới hạn số lượng tùy chọn cấu hình bạn có thể có (vì số lượng cột trong một hàng thường bị giới hạn). Mỗi tùy chọn cấu hình bổ sung yêu cầu thay đổi lược đồ DB.
  • Trong bảng cặp tên-giá trị, mọi thứ đều được "gõ theo chuỗi" (bạn phải mã hóa / giải mã các tham số Boolean / Date / etc.).
  • (nhiều hơn nữa)

Có một số sự đồng thuận trong cộng đồng phát triển về lựa chọn nào là thích hợp hơn?


2
Không có lý do gì phương pháp 'dọc' không thể có các kiểu dữ liệu khác nhau. Thêm một cột int, float và cột văn bản trên mỗi hàng. Lưu / tải các giá trị từ nó bằng các hàm cụ thể theo loại, chẳng hạn như 'SaveConfigInt (' trường ', n)'
GrandmasterB

4
Có một câu hỏi StackOverflow tuyệt vời hỏi về điều này, và câu trả lời hàng đầu đưa ra những ưu và nhược điểm cho cả hai phương pháp. stackoverflow.com/questions/2300356/ trộm
Kevin

1
Cách tiếp cận 3: Cột đơn / Hàng đơn với định dạng trao đổi dữ liệu đơn giản như JSON hoặc YAML. Kết hợp lợi thế từ cả hai phương pháp.
schlamar

Còn việc sử dụng bảng một hàng với dữ liệu phức tạp chứa xml / json, chẳng hạn như <config> <CompanyName> ACME Inc. </ CompanyName> <StartFullScreen> true </ StartFullScreen> 20 <RefreshSeconds> </ RefreshSeconds> </ config> xác thực đối tượng trong lớp nghiệp vụ?
Giăng

1
@ John: Ý tưởng tốt, nếu cần cấu trúc phân cấp. Nếu họ không, đó chỉ là tùy chọn 2 với sự phức tạp thêm.
Heinzi

Câu trả lời:


15

Cá nhân tôi thích các bảng một hàng cho hầu hết mọi thứ. Mặc dù đúng là nó kém linh hoạt, trừ khi bạn đang mong đợi hành vi động, thì việc thêm các cột bổ sung sau này là hoàn toàn chấp nhận được nếu bạn cần. Nói cách khác, nó tương đương với việc sử dụng từ điển / bản đồ để giữ các cặp giá trị tên so với việc có các thành viên lớp khi lập trình. Cấp, nó không phải là một phép ẩn dụ hoàn hảo, nhưng nhiều ưu điểm và nhược điểm song song khi bạn nghĩ về nó.

Vì vậy, bạn sẽ sử dụng một từ điển / bản đồ trên các thành viên lớp? Có lẽ là không trừ khi bạn có lý do để nghĩ rằng lượng dữ liệu được trình bày là hoàn toàn có thể thích ứng, giống như có một bảng cặp giá trị tên.


Điều gì xảy ra nếu dữ liệu được lưu trữ là do người dùng định nghĩa? tức là nghĩ về một giao diện người dùng nơi người dùng có thể tạo một "trường" bằng cách chỉ định nhãn trường, loại dữ liệu mà nó sẽ giữ, v.v. Điều đó có nghĩa là thực thi các câu lệnh DDL từ mã. Bạn vẫn sẽ đi với tùy chọn 1 chứ?
devanalyst

1
@devanalyst Không, nếu dữ liệu có thể thay đổi từ thành phần này sang thành phần khác, thì sẽ không có ý nghĩa gì khi cố gắng tạo một bảng tĩnh để thể hiện nó. Trong trường hợp đó, tốt hơn là sử dụng tùy chọn thứ hai.
Neil

12

Tôi thường đi với tùy chọn 2 NHƯNG tôi có nhiều cột để thực thi kiểu dữ liệu

ConfigOption   |   textValue    |   DateValue   |   NumericValue

Tùy chọn 1 Có lợi ích bổ sung mà bạn có thể dễ dàng "Hoán đổi" toàn bộ Cấu hình bằng cách thêm ActiveCột.


Nếu bạn sẽ cho phép cấu hình bị vô hiệu hóa (đối với tùy chọn 1), ít nhất hãy biến nó thành activatedOndấu thời gian, để bạn có thể biết khi nào nó được kích hoạt. Và nếu bạn đang sử dụng tùy chọn 2 ... điều gì sẽ xảy ra nếu cuối cùng lưu trữ các giá trị trong nhiều cột (hoặc đó là lời tiên tri, trong đó (rõ ràng) null và một chuỗi rỗng là tương đương)?
Clockwork-Muse

1
@ X-Zero, lưu trữ nhiều Configs thường được thực hiện cho mục đích thử nghiệm, nhưng thời gian không thể khắc phục được. Cấu hình bảo trì, gọi để nhận giá trị sẽ biết cột nào cần kiểm tra, nếu bạn thực sự muốn, bạn có thể thêm một cột cho kiểu dữ liệu .. Nhưng tôi nghĩ rằng đó là kết thúc ...
Morons

5
lược đồ EATV (Thực thể-Thuộc tính-Loại-Giá trị) phá vỡ hình thức bình thường thứ ba; cột Loại chỉ liên quan gián tiếp đến khóa chính của bảng, thông qua cột Giá trị mà cột Loại mô tả. Ngoài ra, lưu trữ kiểu động và khởi tạo không giải quyết được nhiều; nếu một phương thức GetConfigValue () có thể trả về bất kỳ loại nào, thì nó phải trả về Object (hoặc được cung cấp loại dự kiến ​​bằng cách nào đó) vẫn phải được đánh giá khi chạy.
KeithS

5
Mỗi lần tùy chọn 1 được triển khai trong phần mềm tôi đã thấy, nó phải được chuyển đổi sang tùy chọn 2. Tùy chọn 2 dễ duy trì hơn theo thời gian, chỉ cần một lần chạm nhiều thời gian hơn để thực hiện chính xác ngay lần đầu tiên. Tùy chọn 1 nhanh chóng và dễ thực hiện nhưng bảo trì theo thời gian là khủng khiếp trừ khi phần mềm của bạn nhỏ bé không có cơ hội phát triển.
Jimmy Hoffa

8

Đối với tôi, việc bạn đi một hàng hay EAV tùy thuộc vào cách bạn muốn tiêu thụ chúng.

Sức mạnh của EAV là dữ liệu mới có thể được thêm vào mà không thay đổi cấu trúc. Điều này có nghĩa là nếu bạn muốn một giá trị cấu hình mới, bạn chỉ cần thêm nó vào bảng và kéo nó ra nơi bạn muốn mã đó và bạn không cần thêm trường mới vào miền, lược đồ, ánh xạ, truy vấn DAL , Vân vân.

Lỗ hổng của nó là nó chỉ có cấu trúc chắc chắn nhất, đòi hỏi bạn phải xử lý dữ liệu một cách bi quan. Mọi việc sử dụng bất kỳ giá trị cấu hình nào đều phải mong muốn giá trị không xuất hiện hoặc không ở định dạng phù hợp và hoạt động tương ứng khi không. Giá trị cấu hình có thể không được phân tích cú pháp thành gấp đôi, hoặc int hoặc char. Nó có thể là null. có thể không có hàng cho giá trị nào cả. Các cách xoay quanh điều này thường yêu cầu một giá trị "mặc định" hợp lệ duy nhất tồn tại cho tất cả các giá trị cấu hình của một loại mã cụ thể ( cực kỳ hiếm; thường thì giá trị mặc định cũng không có vấn đề gì đối với việc tiêu thụ mã như không) giữ một từ điển được mã hóa cứng của các giá trị mặc định (phải thay đổi mỗi khi một cột mới được thêm vào, làm cho lợi thế chính của lưu trữ EAV trở nên khá tốt).

Một hàng rộng duy nhất là khá nhiều ngược lại. Bạn ánh xạ nó tới một phiên bản duy nhất của một đối tượng Cấu hình với trường / thuộc tính cho mọi giá trị cấu hình tồn tại. Bạn biết chính xác loại giá trị nào sẽ có trong thời gian biên dịch và bạn "không nhanh" trong DAL nếu cột cấu hình không tồn tại hoặc không có giá trị của loại phù hợp, cung cấp cho bạn một vị trí để bắt ngoại lệ dựa trên về vấn đề truy xuất / hydrat hóa cấu hình.

Nhược điểm chính là cần phải thay đổi cấu trúc cho mỗi giá trị mới; cột DB mới, cột mới trong DAL (ánh xạ hoặc truy vấn SQL / SP), cột miền mới, tất cả đều cần thiết để kiểm tra sử dụng đúng cách.

Tình huống thích hợp để sử dụng một trong hai điều này là tình huống trong đó các nhược điểm được giảm thiểu. Đối với tôi, hầu hết các tình huống để mã hóa cấu hình đã kêu gọi thực hiện một hàng đơn. Điều này chủ yếu là vì nếu bạn giới thiệu một giá trị cấu hình hoàn toàn mới chi phối hành vi của một số phần trong chương trình của bạn, bạn đã phải thay đổi mã để sử dụng giá trị cấu hình mới; Tại sao không bật lên đối tượng cấu hình và thêm giá trị được sử dụng?

Nói tóm lại, một lược đồ EAV để lưu trữ cấu hình thực sự không giải quyết được vấn đề mà nó có ý định giải quyết và hầu hết các cách giải quyết cho các vấn đề mà nó đưa ra đều vi phạm DRY.


3

Cụ thể cho các giá trị cấu hình, tôi muốn nói - đi với một hàng đơn. Trừ khi bạn hiện đang trải qua quá trình phát triển, các cột đó có thường xuyên thay đổi không?

Có lẽ tốt nhất để bảo mật kiểu dữ liệu của các giá trị , thay vì mã cho khả năng mở rộng mà bạn không có khả năng có trong thời gian chết giữa các bản phát hành (r) lớn. Bên cạnh đó, việc thêm hoặc xóa một cột chỉ là cách di chuyển dễ dàng nhất. Tôi không thấy đau đầu khi tạo tùy chọn cấu hình mới.

Ngoài ra, bạn cho biết "người dùng" có thể định cấu hình các tùy chọn này mà không cần giới hạn. Họ có cấu hình trên mỗi người dùng? Nếu vậy, tôi sẽ tranh luận mạnh mẽ hơn nữa rằng các tùy chọn cấu hình nên nằm trong các cột - một hàng cho mỗi người dùng. Nó sẽ tiết kiệm rất nhiều đau đầu bảo trì sau này.


2

Nếu khách hàng của bạn có thể xử lý các đoạn JSON (không chỉ là mảng và từ điển, mà còn cả chuỗi đơn giản, số, booleans, giá trị null) thì bạn có thể có một bảng nhiều hàng với tên tùy chọn và giá trị chuỗi chứa JSON. Điều đó cho phép bạn cũng lưu trữ các giá trị có cấu trúc và mã để xử lý các giá trị này đã có sẵn.

Nếu khách hàng của bạn không thể xử lý các đoạn JSON, hãy lấy các máy khách mới.


1

Hàng đơn Ưu: Được xác định rõ. Nhược điểm: Thay đổi cấu hình có thể là một nỗi đau. Di chuyển DB, vv ..

Thực thể-Giá trị Ưu điểm: Siêu linh hoạt, hỗ trợ phát triển cấu hình của bạn. Nhược điểm: Tính toàn vẹn tham chiếu? Kiểm tra thêm trong mã của bạn để xem nếu tài sản tồn tại trước khi bạn có thể làm bất cứ điều gì trên nó.

Tôi sẽ sử dụng cách tiếp cận 2 được hỗ trợ bởi một db không liên quan như Mongo. Nếu có một cái gì đó bạn có thể chắc chắn, sự thay đổi của nó.


1

Sử dụng cả hai!

Sắp xếp các tùy chọn có thể có nhiều trường hợp và các tùy chọn nào là chung.

Bảng một hàng (cấu hình)

  id  |  company_name  |  start_fullscreen  |  refresh_seconds  |  ...
------+----------------+--------------------+-------------------+-------
  4   |  ACME Inc.     |  true              |  20               |  ...

Bảng cặp tên-giá trị (tùy chọn)

  name             |  value          | update_time  
-------------------+-----------------+--------------
  generic_option_1 |  Option 1 Value | timestamp    
  generic_option_2 |  Option 2 Value | timestamp    
  generic_option_3 |  Option 3 Value | timestamp    
  configuration    |  4              | timestamp    
  ...              |  ...            | ...          

Tôi nghĩ rằng điều này là linh hoạt hơn.

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.