views:

467

answers:

7

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 DBS is SQL Server 2008 and programming layer is implemented with asp.net (in C#).

A: 

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
+3  A: 

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
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
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
+2  A: 

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.

EJB
+1  A: 

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
@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
note: he said "save settings and configurations", not "I need to save relational cart data"
Stephano
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
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
I'd go key value pair, dump it all out into a dictionary on startup and your sorted.
Paul Creasey
@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
@David Murdoch I certainly quoted one of the more popular SO questions :) .
Stephano
+1  A: 

As you guessed, and except for the for 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.

mjv
It sounds like overkill for most uses of a configuration table.
JerryOL
+2  A: 

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
+10  A: 

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.

adrianbanks
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
Single row positive: Can have FK defined on the some columns!
wqw
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
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