Sử dụng bảng cấu hình một hàng trong cơ sở dữ liệu SQL Server. Ý tưởng tồi?


145

Khi phát triển ứng dụng giỏ hàng, tôi thấy rằng tôi cần lưu các cài đặt và cấu hình dựa trên các yêu cầu và yêu cầu của quản trị viên. Thông tin này có thể là bất cứ thứ gì từ thông tin công ty, ID tài khoản vận chuyển, khóa API PayPal, tùy chọn thông báo, v.v.

Có vẻ rất không phù hợp để tạo một bảng để lưu trữ một hàng trong hệ thống cơ sở dữ liệu quan hệ.

Cách thích hợp để lưu trữ thông tin này là gì?

Lưu ý: DBMS của tôi là SQL Server 2008 và lớp lập trình được triển khai với ASP.NET (trong C #).

Câu trả lời:


189

Tôi đã thực hiện hai cách này trong quá khứ - một bảng hàng đơn và bảng cặp khóa / giá trị - và có những mặt tích cực và tiêu cực đối với mỗi cách tiếp cận.

Hàng đơn

  • dương: các giá trị được lưu trữ đúng loại
  • tích cực: dễ dàng hơn để xử lý mã (do những điều trên)
  • positive: các giá trị mặc định có thể được cung cấp cho từng cài đặt riêng lẻ
  • phủ định: cần phải thay đổi lược đồ để thêm cài đặt mới
  • tiêu cực: bảng có thể trở nên rất rộng nếu có nhiều cài đặt

Cặp khóa / giá trị

  • tích cực: thêm cài đặt mới không yêu cầu thay đổi lược đồ
  • tích cực: lược đồ bảng hẹp, với các hàng bổ sung được sử dụng cho các cài đặt mới
  • phủ định: mỗi cài đặt có cùng giá trị mặc định (null / trống?)
  • phủ định: mọi thứ phải được lưu trữ dưới dạng chuỗi (ví dụ: nvarchar)
  • phủ định: khi xử lý các cài đặt trong mã, bạn phải biết loại cài đặt là gì và truyền nó

Tùy chọn hàng đơn cho đến nay là cách dễ nhất để làm việc. Điều này là do bạn có thể lưu trữ từng cài đặt theo loại chính xác trong cơ sở dữ liệu và không phải lưu trữ các loại cài đặt cũng như các khóa tra cứu của chúng trong mã.

Một điều tôi quan tâm khi sử dụng phương pháp này là có nhiều hàng trong bảng cài đặt hàng đơn "đặc biệt". Tôi đã khắc phục điều này bằng cách (trong SQL Server):

  • thêm một cột bit mới với giá trị mặc định là 0
  • tạo một ràng buộc kiểm tra để đảm bảo rằng cột này có giá trị 0
  • tạo một ràng buộc duy nhất trên cột bit

Điều này có nghĩa là chỉ có một hàng có thể tồn tại trong bảng vì cột bit phải có giá trị 0, nhưng chỉ có thể có một hàng với giá trị đó do ràng buộc duy nhất.


5
Chúng tôi làm điều đơn hàng trong ứng dụng LOB của chúng tôi. Các giá trị là tất cả các loại chính xác, điều này làm cho việc sử dụng chúng trong ứng dụng đơn giản hơn nhiều. Lược đồ của chúng tôi được phiên bản cùng với ứng dụng, do đó, một thay đổi đối với thiết lập cấu hình được quản lý giống như bất kỳ sửa đổi ứng dụng nào.
DaveE

17
Hàng đơn dương: Có thể có FK được xác định trên một số cột!
wqw

8
Bạn luôn có thể thực hiện một cặp khóa / giá trị với một mã định danh loại để xác định cột nào có giá trị trong loại giá trị của nó. Điều này cung cấp cho bạn tốt nhất của cả hai thế giới và bạn có thể sử dụng một Proc được lưu trữ để nhận được giá trị khi bạn cần.
Middletone

19
Một điều thực sự có thể làm hỏng ngày của bạn sau khi thực hiện giải pháp một hàng là khi sau đó bạn được giao nhiệm vụ "hãy theo dõi lần cuối mỗi giá trị được thay đổi và ai đã thay đổi nó ...."
Dave Mateer

6
Một ưu điểm khác của giải pháp một hàng mà tôi đã phát hiện ra trong một trường hợp: Tôi đã có một ứng dụng được xây dựng cho một khách hàng, với một bảng một hàng cho "cài đặt". Sau này tôi có hai khách hàng khác muốn sử dụng cùng một ứng dụng, nhưng muốn có các cài đặt khác nhau: tất cả những gì tôi phải làm là thêm PK "client_id" vào bảng để duy trì một bộ cài đặt riêng cho từng khách hàng. (Đây là khi bạn nhận ra rằng các "cài đặt" này thực sự chỉ là thuộc tính cho một thực thể cấp cao hơn mà bạn chưa được mô hình hóa.)
Jeffrey Kemp

10

Bạn nên tạo một bảng có một cột cho loại thông tin và giá trị thông tin (ít nhất). Bằng cách này, bạn tránh phải tạo các cột mới mỗi khi thêm thông tin mới.


1
Đơn giản và gọn gàng. Chỉ cần làm việc với một danh sách các cặp giá trị chính từ đó. Bạn có thể muốn nghĩ về các giá trị mặc định một chút, tùy thuộc vào bối cảnh sử dụng ...
Paul Kohler

4
Tại sao nó là một vấn đề để tạo cột mới? Tôi biết có những tình huống mà các nhà phát triển phải tránh nó vì các vấn đề chính trị với việc cập nhật các lược đồ SQL, nhưng không có đề cập đến vấn đề đó trong câu hỏi.
vây

6

Một hàng duy nhất sẽ hoạt động tốt; Nó thậm chí sẽ có các loại mạnh:

show_borders    bit
admin_name      varchar(50)
max_users       int

Một nhược điểm là nó yêu cầu thay đổi lược đồ ( alter table) để thêm cài đặt mới. Một cách khác là bình thường hóa, nơi bạn kết thúc với một bảng như:

pref_name       varchar(50) primary key
pref_value      varchar(50) 

Điều này có các loại yếu (tất cả mọi thứ là một varchar), nhưng thêm một cài đặt mới chỉ là thêm một hàng, điều bạn có thể làm chỉ với quyền truy cập ghi cơ sở dữ liệu.


4

Cá nhân, tôi sẽ lưu trữ nó trong một hàng duy nhất nếu đó là những gì hoạt động. Quá mức cần thiết để lưu trữ nó trong một bảng SQL? có lẽ, nhưng không có hại thực sự khi làm như vậy.


4

Như bạn đã đoán, và ngoại trừ các tình huống đơn giản nhất, việc đặt tất cả các tham số cấu hình trong một hàng có nhiều nhược điểm. Nó là một ý kiến ​​không hay...

Một cách thuận tiện để lưu trữ cấu hình và / hoặc loại thông tin tùy chọn người dùng có trong XML . Nhiều DBMS hỗ trợ kiểu dữ liệu XML. Cú pháp XML cho phép bạn sử dụng "ngôn ngữ" và cấu trúc mô tả cấu hình khi cấu hình này phát triển. Một lợi thế của XML là sự hỗ trợ ngầm định của nó đối với cấu trúc phân cấp, ví dụ cho phép lưu trữ các danh sách nhỏ các tham số cấu hình mà không phải đặt tên chúng với hậu tố được đánh số. Một nhược điểm có thể có của định dạng XML là việc tìm kiếm và sửa đổi dữ liệu này không đơn giản như các cách tiếp cận khác (không có gì phức tạp, nhưng không đơn giản / tự nhiên)

Nếu bạn muốn tiếp tục gần hơn với mô hình quan hệ , mô hình Entity-Attribution-Value có lẽ là thứ bạn cần, theo đó các giá trị riêng lẻ được lưu trữ trong một bảng thường trông như sau:

EntityId     (foreign key to the "owner" of this attribute)
AttributeId  (foreign key to the "metadata" table where the attribute is defined)
StringValue  (it is often convenient to have different columns of different types
IntValue      allowing to store the various attributes in a format that befits 
              them)

Trong đó AttributionId là khóa ngoại đối với bảng nơi mỗi thuộc tính có thể ("tham số cấu hình" trong trường hợp của bạn) được xác định, với giả sử

AttributeId  (Primary Key)
Name
AttributeType     (some code  S = string, I = Int etc.)
Required          (some boolean indicating that this is required)
Some_other_fields   (for example to define in which order these attributes get displayed etc...)

Cuối cùng, EntityId cho phép bạn xác định một số thực thể "sở hữu" các thuộc tính khác nhau này. Trong trường hợp của bạn, đó có thể là UserId hoặc thậm chí chỉ ẩn nếu bạn chỉ có một cấu hình để quản lý.

Ngoài việc cho phép danh sách các tham số cấu hình có thể phát triển khi ứng dụng phát triển, mô hình EAV đặt "dữ liệu meta", tức là dữ liệu liên quan đến chính Thuộc tính, trong dữ liệu, do đó tránh được tất cả các tên cột được mã hóa cứng thường thấy khi các tham số cấu hình được lưu trữ trong một hàng đơn.


3
Nghe có vẻ như quá mức cho hầu hết việc sử dụng bảng cấu hình.
JerryOL

Tôi nghĩ rằng ý tưởng chung đằng sau phương pháp này là tuyệt vời. Nhưng tại sao XML? Chỉ cần chọn một định dạng trao đổi dữ liệu đơn giản như JSON hoặc YAML và bạn có thể có những lợi thế từ cả hai biến thể khác.
schlamar

1
EAV là quan hệ nhưng nó không được chuẩn hóa. Chắc chắn có các trường hợp sử dụng cho nó (ví dụ các hệ thống ORM có vẻ thích chúng), nhưng lập luận rằng dữ liệu meta có trong cơ sở dữ liệu cho EAV không phải là lý do thuyết phục để sử dụng nó. Tất cả RDBMS đều chứa dữ liệu meta trong các bảng hệ thống mà bạn có thể khám phá, do đó các bảng hàng đơn cũng do đó có siêu dữ liệu trong cơ sở dữ liệu. Tên cột được mã hóa cứng cũng không phải là vấn đề. Nếu bạn sử dụng khóa cho các thực thể và thuộc tính thì bạn đã có một bảng tra cứu được mã hóa cứng ở một nơi khác xác định chúng (hoặc tệ hơn là trong lớp trình bày của bạn).
Davos

3

Bạn chắc chắn không phải thay đổi lược đồ của mình khi thêm một tham số cấu hình mới theo cách tiếp cận chuẩn hóa, nhưng có lẽ bạn vẫn thay đổi mã của mình để xử lý giá trị mới.

Thêm một "bảng thay đổi" vào việc triển khai của bạn dường như không phải là một sự đánh đổi lớn vì sự đơn giản và an toàn của kiểu tiếp cận hàng đơn.


2

Cặp Khóa và Giá trị tương tự như .Net App.Config có thể lưu trữ cài đặt cấu hình.

Vì vậy, khi bạn muốn lấy giá trị bạn có thể làm:

SELECT value FROM configurationTable
WHERE ApplicationGroup = 'myappgroup'
AND keyDescription = 'myKey';

1

Một cách phổ biến để làm điều này là có một bảng "thuộc tính" mô phỏng theo tệp thuộc tính. Tại đây bạn có thể lưu trữ tất cả các hằng số ứng dụng của mình, hoặc không phải là những thứ liên tục mà bạn chỉ cần có xung quanh.

Sau đó, bạn có thể lấy thông tin từ bảng này khi bạn cần. Tương tự, khi bạn thấy bạn có một số cài đặt khác để lưu, bạn có thể thêm nó vào. Dưới đây là một ví dụ:

property_entry_table

[id, scope, refId, propertyName, propertyValue, propertyType] 
1, 0, 1, "COMPANY_INFO", "Acme Tools", "ADMIN"  
2, 0, 1, "SHIPPING_ID", "12333484", "ADMIN"  
3, 0, 1, "PAYPAL_KEY", "2143123412341", "ADMIN"   
4, 0, 1, "PAYPAL_KEY", "123412341234123", "ADMIN"  
5, 0, 1, "NOTIF_PREF", "ON", "ADMIN"  
6, 0, 2, "NOTIF_PREF", "OFF", "ADMIN"   

Bằng cách này, bạn có thể lưu trữ dữ liệu bạn có và dữ liệu bạn sẽ có trong năm tới và chưa biết về :).

Trong ví dụ này, phạm vi và refId của bạn có thể được sử dụng cho bất cứ điều gì bạn muốn ở mặt sau. Vì vậy, nếu propertyType "ADMIN" có phạm vi 0 refId 2, bạn sẽ biết nó ưu tiên gì.

Loại tài sản có trong tay khi một ngày nào đó, bạn cũng cần lưu trữ thông tin không phải quản trị viên ở đây.

Lưu ý rằng bạn không nên lưu trữ dữ liệu giỏ hàng theo cách này hoặc tra cứu cho vấn đề đó. Tuy nhiên nếu dữ liệu là Hệ thống cụ thể, thì bạn chắc chắn có thể sử dụng phương pháp này.

Ví dụ: Nếu bạn muốn lưu trữ DATABASE_VERSION của mình , bạn sẽ sử dụng một bảng như thế này. Bằng cách đó, khi bạn cần nâng cấp ứng dụng, bạn có thể kiểm tra bảng thuộc tính để xem phiên bản phần mềm của máy khách của bạn.

Vấn đề là bạn không muốn sử dụng cái này cho những thứ liên quan đến giỏ hàng. Giữ cho bạn logic kinh doanh trong các bảng quan hệ được xác định rõ. Bảng thuộc tính chỉ dành cho thông tin hệ thống.


@finnw Tôi hoàn toàn đồng ý rằng phương pháp này không nên được sử dụng để tra cứu, đặc biệt là khi có rất nhiều loại tra cứu khác nhau. Có lẽ tôi đã hiểu nhầm câu hỏi. Có vẻ như anh ta cần một bảng cho các hằng số và thuộc tính hệ thống. Trong trường hợp đó, tại sao có 10 bảng khác nhau?
Stephano

lưu ý: ông nói "lưu cài đặt và cấu hình", chứ không phải "Tôi cần lưu dữ liệu giỏ hàng quan hệ"
Stephano

Tôi phản đối điều này là bạn đang bỏ qua việc gõ SQL và các cơ chế ràng buộc khác để tránh cập nhật lược đồ SQL khi bạn thêm các thuộc tính mới. Như bạn nói "dữ liệu mà bạn sẽ có trong năm tới và chưa biết về nó." Có, bạn sẽ có dữ liệu mới vào năm tới, nhưng điều gì ngăn bạn tạo các cột SQL (đã gõ) mới, KIỂM TRA và có thể là các ràng buộc FOREIGN KEY cho nó tại thời điểm nó được thêm vào?
vây

Bản năng đầu tiên của tôi là chỉ cần thêm dữ liệu này vào một tệp phẳng. Và bạn đã đúng, quá trình sử dụng bảng này thực sự sẽ phá vỡ các cơ chế ràng buộc của DBMS. Tuy nhiên, tôi sẽ nói rằng nếu bạn cố gắng quá nhiều để làm theo các kỹ thuật cơ sở dữ liệu phù hợp, bạn đang thiếu điểm. Kiểm tra câu trả lời đầu tiên; được bình chọn cao nhất trên SO: stackoverflow.com/questions
406760 / Cách

2
Tôi sẽ đi cặp giá trị chính, đổ tất cả vào một từ điển khi khởi động và sắp xếp của bạn.
Paul Creasey

0

Tôi không chắc chắn một hàng duy nhất là triển khai tốt nhất cho cấu hình. Bạn có thể tốt hơn khi có một hàng cho mỗi mục cấu hình với hai cột (configName, configValue), mặc dù điều này sẽ yêu cầu chuyển tất cả các giá trị của bạn thành chuỗi và ngược lại.

Bất kể, không có hại trong việc sử dụng một hàng cho cấu hình toàn cầu. Các tùy chọn khác để lưu trữ nó trong DB (biến toàn cục) là tồi tệ hơn. Bạn có thể kiểm soát nó bằng cách chèn hàng cấu hình đầu tiên của bạn, sau đó vô hiệu hóa các phần chèn trên bảng để ngăn nhiều hàng.


0

Bạn có thể thực hiện Cặp khóa / giá trị mà không cần chuyển đổi bằng cách thêm một cột cho từng loại chính và một cột cho bạn biết dữ liệu nằm trong cột nào.

Vì vậy, bảng của bạn sẽ trông giống như:

id, column_num, property_name, intValue, floatValue, charValue, dateValue
1, 1, weeks, 51, , ,
2, 2, pi, , 3.14159, , 
3, 4, FiscYearEnd, , , , 1/31/2015
4, 3, CompanyName, , , ACME, 

Nó sử dụng nhiều phòng hơn một chút nhưng nhiều nhất bạn đang sử dụng vài chục thuộc tính. Bạn có thể sử dụng một câu lệnh tình huống tắt giá trị cột_num để kéo / nối đúng trường.


0

Xin lỗi tôi đến như thế, yeaars sau. Nhưng dù sao, những gì tôi làm là đơn giản và hiệu quả. Tôi chỉ cần tạo một bảng có ba cột ():

ID - int (11)

tên - varchar (64)

giá trị - văn bản

Những gì tôi làm trước khi tạo một cột cấu hình mới, cập nhật nó hoặc đọc là để tuần tự hóa "giá trị"! Bằng cách này, tôi chắc chắn về loại (Vâng, php là :))

Ví dụ:

b: 0; dành cho B OOLESE ( sai )

b: 1; dành cho B OOLESE ( đúng )

i: 1988; là cho tôi NT

s: 5: "Kader"; dành cho S TRING có độ dài 5 ký tự

Tôi hi vọng cái này giúp được :)


1
Tại sao không chỉ tạo một cột mới cho loại? i:1988có vẻ như bạn đang cố thu gọn hai mẩu thông tin vào một cột.
maksymiuk

@maksymiuk SImply vì một khi chưa được xác thực, bạn có được loại chính xác thay vì sử dụng một vòng lặp sau (nếu hoặc chuyển đổi) ... vv
Kader Bouyakoub

không cần bất kỳ vòng lặp hoặc công tắc hay bất cứ thứ gì, nó thực sự sẽ loại bỏ bước phải phân tích thông tin từ mỗi hàng, trong khi đó, nếu bạn có thêm một cột cho loại, thông tin loại đã có sẵn cho thành phần lấy thông tin mà không phải thực hiện thêm bước nào ngoài chỉ truy vấn ban đầu
maksymiuk

Ý của bạn bằng cách làm một cái gì đó như echo (int) $varcho một số nguyên và những người khác cho các loại khác?
Kader Bouyakoub

0

Có một cột khóa là varchar và cột giá trị là JSON. 1là số trong khi đó "1"là một chuỗi. truefalseđều là boolean. Bạn có thể có các đối tượng là tố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.