165

In developing a shopping cart application I've found that I needed to save settings and configurations based on the administrator's preferences and requirements. This information can be anything from company information, Shipping account IDs, PayPal API keys, notification preferences, etc.

It seems highly inappropriate to create a table to store a single row in a relational database system.

What is the appropriate way to store this information?

Note: my DBMS is SQL Server 2008 and programming layer is implemented with ASP.NET (in C#).

bluish
  • 26,356
  • 27
  • 122
  • 180
David Murdoch
  • 87,823
  • 39
  • 148
  • 191

12 Answers12

210

I have done this two ways in the past - a single row table and a key/value pair table - and there are positives and negatives to each approach.

Single Row

  • positive: the values are stored in the correct type
  • positive: it is easier to deal with in code (due to the above)
  • positive: default values can be given to each setting individually
  • negative: a schema change is required to add a new setting
  • negative: the table can become very wide if there are lots of settings

Key/Value Pair

  • positive: adding new settings does not require a schema change
  • positive: the table schema is narrow, with extra rows being used for new settings
  • negative: each setting has the same default value (null/empty?)
  • negative: everything has to be stored as strings (ie. nvarchar)
  • negative: when dealing with the settings in code, you have to know what type a setting is and cast it

The single row option is by far the easiest one to work with. This is because you can store each setting in its correct type in the database and not have to store the types of the settings as well as their lookup keys in code.

One thing I was concerned with using this approach was having multiple rows in the "special" single row settings table. I overcame this by (in SQL Server):

  • adding a new bit column with a default value of 0
  • creating a check constraint to ensure that this column has a value of 0
  • creating a unique constraint on the bit column

This means that only one row can exist in the table because the bit column has to have a value of 0, but there can only be one row with that value because of the unique constraint.

Community
  • 1
  • 1
adrianbanks
  • 81,306
  • 22
  • 176
  • 206
  • 6
    We do the single-row thing in our LOB application. The values are all of the correct type, which makes using them in the application much simpler. Our schema is versioned along with the application, so a change to the configuration setup gets managed just like any application revision. – DaveE Feb 20 '10 at 01:07
  • 21
    Single row positive: Can have FK defined on the some columns! – wqw Feb 20 '10 at 23:12
  • 1
    The single row solution will work great as schema changes won't be an issue and there won't be too many columns. Thanks for your pro-cons answer as well as the "special" single row explanation. – David Murdoch Feb 22 '10 at 13:36
  • 10
    You can always do a key/value pair with a type identifier to determine which column has the value in its value type. This gives you the best of both worlds and you can use a stored proc to get the value when you need it. – Middletone Feb 23 '10 at 20:34
  • 27
    One thing that can really ruin your day after implementing the single-row solution is when you are later tasked with "let's also keep track of the last time each value was changed and who changed it...." – Dave Mateer Jun 28 '13 at 17:27
  • Approach 3: Single Column/Single Row with a simple data interchange format like JSON or YAML. Combines advantages from both approaches. – schlamar Sep 27 '13 at 06:42
  • 1
    @schlamar: Doing that makes it hard to query the data, unless you use a format that is supported by the database (e.g. XML in SQL Server). – adrianbanks Sep 27 '13 at 09:21
  • 2
    Key Value Negative: If you ever want to report from an Key/Value table, you will need to join to it once for every attribute you want to display. More than 3 and it slows to a crawl. If you never want to report from this table and you are only ever going to look up or write a single attribute at a time, and it's a relatively small table, then the EAV model is fine, otherwise, single row table. – Davos Nov 27 '13 at 03:35
  • 1
    One thing you can do with the Single Row approach is add a "name" field and give the user the ability to save and load their configurations. – Dave Cousineau Jan 19 '15 at 22:48
  • 3
    @DaveMateer: Why would that be a problem? You can always create another "log" table and use a trigger. – Santhos Mar 01 '15 at 19:34
  • 11
    Another advantage of the single-row solution, which I discovered in one case: I had an application built for one client, with a single-row table for "settings". I later got two other clients who wanted to use the same application, but wanted different settings: all I had to do was add a "client_id" PK to the table in order to maintain a separate set of settings for each client. (This is when you realise that these "settings" are really just attributes for a higher-level entity you haven't modelled yet.) – Jeffrey Kemp Mar 16 '15 at 00:40
  • 1
    "•negative: everything has to be stored as strings (ie. nvarchar)" Not true; you can use the sql_variant data type, which stores the type along with its value. – Andy Apr 29 '15 at 12:38
  • 2
    Single row negative: while I prefer single row, there can be situations where, if you're trying to update settings from multiple places on a busy system, you can run into row level locking situations because SQL locks the whole row on an update. Not an insurmountable problem, just a comment. – Nils May 15 '15 at 10:08
  • Key/Value Pair negative: you have to know the name of the properties exactly to be referred in code – shabby Jun 17 '15 at 07:53
  • Why "adding a new bit column with a default value of 0"? You have a primary key, right? Why not use that column? – user1032531 Oct 13 '15 at 11:58
  • @schlamar your approach violates https://en.wikipedia.org/wiki/First_normal_form . – Victor Yarema Nov 23 '15 at 22:03
  • what about using single-row table with companyID/accountID and complex data containing xml/json such as ACME Inc.true 20 and validate object in business layer? – mko Feb 26 '16 at 22:38
  • @DaveMateer you can still do auditing on single row, just have a separate auditing table to track these changes. – lastlink Jul 25 '19 at 15:47
  • For dynamic forms i.e. lets say you allow user to define form fields with data type, precision, length etc. e.g. "Amount" field with numeric data type with precision of two decimal values, the single row approach would mean executing DDL from code. Wouldn't that be a security risk ? – devanalyst Oct 05 '19 at 12:36
11

You should create a table with a column for the information type and information value (at least). This way you avoid having to create new columns every time a new information is added.

Otávio Décio
  • 73,752
  • 17
  • 161
  • 228
  • 1
    Simple and neat. Just work with a list of key value pairs from there. You may want to think about default values a bit, depends on the context of use... – Paul Kohler Feb 20 '10 at 00:12
  • 4
    Why is it a problem to create new columns? I know there are situations where developers must avoid it because of political issues with updating SQL schemas, but there is no mention of that in the question. – finnw Feb 20 '10 at 01:29
6

A single row will work fine; it will even have strong types:

show_borders    bit
admin_name      varchar(50)
max_users       int

One disadvantage is that it requires a schema change (alter table) to add a new setting. One alternative is normalizing, where you end up with a table like:

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

This has weak types (everything is a varchar), but adding a new setting is just adding a row, something you can do with just database write access.

Andomar
  • 232,371
  • 49
  • 380
  • 404
5

Personally, I would store it in a single row if that is what works. Overkill to store it in an SQL table? probably, but there is no real harm in doing so.

E.J. Brennan
  • 45,870
  • 7
  • 88
  • 116
4

As you guessed, and except for the simplest situations, putting all configurations parameters in a single rows has many drawbacks. It is a bad idea...

A convenient way to store configuration and/or user preference type of information is in XML. Many DBMSes support the XML data type. The XML syntax allows you to expend the "language" and structure describing the configuration as this configuration evolves. One advantage of XML is its implicit support for hierarchical structure, allowing for example to store small lists of configuration parameters without having to name these with a numbered suffix. A possible drawback of XML format is that searching and generally modifying this data isn't as straight forward as other approaches (nothing complicated, but not as simple/natural)

If you want to remain closer to relational model, the Entity-Attribute-Value model is probably what you need, whereby the individual values are stored in a table that typically looks like:

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)

Whereby the AttributeId is a foreign key to a table where each possible Attribute ("configuration parameter" in your case) is defined, with say

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...)

Finally the EntityId allows you to identify some entity which "owns" these various attributes. In your case it could be a UserId or even just implicit if you only have one configuration to manage.

Aside from allowing the list of possible configuration parameters to grow as the application evolves, the EAV model places the "meta data", i.e. the data pertaining to the Attribute themselves, in datatables, hence avoiding all the hard-coding of column names commonly seen when the configuration parameters are stored in a single row.

bluish
  • 26,356
  • 27
  • 122
  • 180
mjv
  • 73,152
  • 14
  • 113
  • 156
  • 3
    It sounds like overkill for most uses of a configuration table. – JerryOL Jun 09 '10 at 02:21
  • I think the general idea behind this approach is great. But why XML? Just pick a simple data interchange format like JSON or YAML and you can have the advantages from both of the other variations. – schlamar Sep 27 '13 at 06:37
  • 1
    EAV is relational but it's not normalized. There are certainly use cases for it (for example ORM systems seem to love them), but the argument that the meta data is in the database for EAV is not a convincing reason to use it. All RDBMS contain meta data in system tables anyway that you can discover, so single row tables therefore also have metadata in the database. Hard-coded column names are also a non-issue. If you use keys for entities and attributes then you've got a hard-coded look-up table somewhere else that defines them (or worse it's in your presentation layer). – Davos Nov 27 '13 at 03:45
3

You certainly don't have to change your schema when adding a new configuration parameter in the normalized approach, but you're still probably changing your code to process the new value.

Adding an "alter table" to your deployment doesn't seem like that big of a tradeoff for the simplicity and type safety of the single row approach.

2

A Key and Value pair is similar to a .Net App.Config which can store configuration settings.

So when you want to retrieve the value you could do:

SELECT value FROM configurationTable
WHERE ApplicationGroup = 'myappgroup'
AND keyDescription = 'myKey';
Adriano Carneiro
  • 57,693
  • 12
  • 90
  • 123
rizalp1
  • 6,346
  • 2
  • 17
  • 19
1

A common way to do this is to have a "properties" table simmular to a properties file. Here you can store all your app constants, or not so constant things that you just need to have around.

You can then grab the info from this table as you need it. Likewise, as you find you have some other setting to save, you can add it in. Here is an example:

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"   

This way you can store the data you have, and the data that you will have next year and don't know about yet :) .

In this example, your scope and refId can be used for whatever you want on the back end. So if propertyType "ADMIN" has a scope 0 refId 2, you know what preference it is.

Property type comes in hand when, someday, you need to store non-admin info in here as well.

Note that you should not store cart data this way, or lookups for that matter. However if the data is System specific, then you can certainly use this method.

For example: If you want to store your DATABASE_VERSION, you'd use a table like this. That way, when you need to upgrade the app, you can check the properties table to see what version of your software the client has.

The point is you do not want to use this for things that pertain to the cart. Keep you business logic in well defined relational tables. The properties table is for system info only.

Stephano
  • 5,716
  • 7
  • 41
  • 57
  • @finnw I totally agree that this method should not be used for lookups, especially when there are lots of different types of lookups. Perhaps I misunderstood the question. It sounded like he needed a table for constants and system properties. In that case, why have 10 different tables? – Stephano Feb 20 '10 at 00:32
  • note: he said "save settings and configurations", not "I need to save relational cart data" – Stephano Feb 20 '10 at 00:39
  • My objection to this is that you are bypassing SQL's typing and other constraint mechanisms to avoid updating the SQL schema when you add new attributes. As you say "data that you will have next year and don't know about yet." Yes you will have new data next year, but what's to stop you creating new (typed) SQL columns, CHECK and possibly FOREIGN KEY constraints for it at the time it is added? – finnw Feb 20 '10 at 01:26
  • My first instinct is to simply add this data to a flat file. And you are correct, this process of using a table instead will indeed circumvent constraint mechanisms of the DBMS. However, I would say that if you try too hard to follow proper database techniques, you are missing the point. Check out the first answer; highest voted on SO: http://stackoverflow.com/questions/406760/whats-your-most-controversial-programming-opinion – Stephano Feb 20 '10 at 04:43
  • 2
    I'd go key value pair, dump it all out into a dictionary on startup and your sorted. – Paul Creasey Feb 20 '10 at 11:19
  • @Stephano, but has that question been upvoted so many times because people disagree with it or because they agree with it? j/k :-) – David Murdoch Feb 22 '10 at 15:42
  • @David Murdoch I certainly quoted one of the more popular SO questions :) . – Stephano Feb 22 '10 at 17:05
1

You can do the Key/Value Pair without conversions by adding a column for each major type and one column telling you which column the data is in.

So your table would look something like:

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, 

It uses a little more room but at most you are using a few dozen attributes. You can use a case statement off the column_num value to pull / join the right field.

spintool
  • 11
  • 1
1

Have a key column as varchar and a value column as JSON. 1 is numeric whereas "1" is a string. true and false are both boolean. You can have objects as well.

kzh
  • 19,810
  • 13
  • 73
  • 97
0

I'm not sure a single row is the best implementation for configuration. You might be better off having a row per configuration item with two columns (configName, configValue), although this will require casting all of your values to strings and back.

Regardless, there's no harm in using a single row for global config. The other options for storing it in the DB (global variables) are worse. You could control it by inserting your first configuration row, then disabling inserts on the table to prevent multiple rows.

sidereal
  • 1,072
  • 7
  • 15
0

Sorry I come like, yeaars later. But anyways, what I do is simple and effective. I simply create a table with three () columns:

ID - int (11)

name - varchar (64)

value - text

What I do before creating a new config column, updating it or reading is to serialize the "value"! This way I am sure of the type (Well, php is :) )

For instance:

b:0; is for BOOLEAN (false)

b:1; is for BOOLEAN (true)

i:1988; is for INT

s:5:"Kader"; is for a STRING of 5 characters length

I hope this helps :)

Kader Bouyakoub
  • 398
  • 2
  • 11
  • 1
    Why not just create a new column for the type? `i:1988` seems like you are trying to collapse two pieces of information into a single column. – maraaaaaaaa Feb 13 '16 at 20:46
  • @maksymiuk SImply because once unserialized you get the exact type instead of using a loop after (if or switch) ... etc – Kader Bouyakoub Apr 29 '16 at 21:33
  • no need for any loops or switches or anything, it would actually remove the step of having to parse the information from each row, whereas, if you had an extra column for the type, the type information is already available to the component pulling the information without having to do any further steps than just the initial query – maraaaaaaaa May 03 '16 at 14:31
  • Your mean by doing something like `echo (int) $var` for an integer and others for other types? – Kader Bouyakoub May 07 '16 at 07:47