Alternative to multi-valued fields in MS Access - database

Related question: Multivalued Fields a Good Idea?
I know that multi-valued fields are similar to many-to-many relationship. What is the best way to replace multi-valued fields in an MS Access application?
I have an application that has multi-valued fields. I am not sure how exactly to do away with those and implement exactly same logic in the form of fields that are single-valued?
What would be the implementation in terms of table-relationships when I want to move a multi-valued relationship to single-valued one.
Thank you.

The following is probably far more detailed than you need, but it is intended for a beginner. Let us say you have a table, MainTable:
ID -> Numeric, primary key
Title -> Text
Surname -> Text
Address -> Text
Country -> Numeric
You will probably want a list of titles and countries from which to select.
In the case of Title, it would not be the worst thing to store the information in a field in a table, because you have a single column and the data is unlikely to change, and you probably will not be creating a query using the data.
Country is a different story, conventionally you would store a number and have a look-up table. It is the kind of situation where people are tempted to use a multi-value field.
However, convention is a lot easier. Add another table for country:
ID -> Numeric, primary key
Country -> Text
You might like to call the related field in the main table CountryID. You can now create a relationship in the relationship window showing how Country relates to MainTable:
You can see that Enforce Referential Integrity is selected, which means that you must have null or a country from the countries table in the CountryID field.
To view the data, you can create a query:
SELECT
MainTable.ID,
MainTable.Title,
MainTable.Surname,
MainTable.Address,
Country.Country
FROM Country
INNER JOIN MainTable
ON Country.ID = MainTable.CountryID;
But the main point is to have a form that allows data entry. You can create a form using the wizards, but after that, you either need to right-click CountryID and change it to a combobox or add a combobox or listbox using the wizard. Option 2 is probably the easiest. Here are most of the steps from the wizard:
You now have a dropdown list of countries on your form.
See also: create form to add records in multiple tables
In Access 2010, there are new ways of adding values to combos when the user enters data that does not exist in the table of possible values. In previous versions (although I am not sure about 2007), you would use the Not In List event to add items to a look-up table, in 2010, you have a choice of adding a List Items Edit form to the property sheet.

The question is rather odd since it asks about a single-value field, but also asks about table-relationships. In a very strict interpretation, a multi-value field (MVF) could be replaced with a single TextBox filled with comma-separated items... no table-relationships required. Instead, I assume by "single-value" field that the question means standard fields in a multi-table relationship, in which each field of each related row has a single value. But each primary record can still be related to multiple rows in the related value table, which preserves the whole purpose of the MVF.
Consider the database outline below to illustrate a possible replacement for the MVF. I'm not including every possible property or how to create basic object, just what's necessary for creating desired behavior--assuming enough knowledge of Access to "fill in the blanks" and no fear of basic code or SQL.
The basic structure consists of three tables: 1) Primary table, 2) "value list" table, 3) junction table used to define many-to-many relationship.
Customer Table
CustomerID: autonumber, primary
CustomerName: short text
Codes Table
Code: short text, length 5, primary key
Description: short text
[Customer Codes] Table
CustomerID: long
Code: short text
Create relationships between the tables. This will require appropriate indexes defined on the tables (not detailed here).
Customer Table to [Customer Codes] Table
CustomerID -> CustomerID fields (with enforce integrity enabled)
Code Table to [Customer Codes] Table
Code -> Code fields (with enforce integrity enabled)
(A separate ID field could also be created for the values table [e.g. Codes Table] table, but that just complicates later queries and controls, etc. In such a case, the junction table would contain another ID field and not the value directly.)
In a standard VBA module, create a function like
Public Function GetCodeList(ByVal CustomerID As Integer) As String
Dim sSQL As String
Dim qry As QueryDef
Dim rs As Recordset2
sSQL = "PARAMETERS [CustID] LONG;" & _
" SELECT * FROM [Customer Codes] WHERE [CustomerID] = [CustID]"
Set qry = CurrentDb.CreateQueryDef("", sSQL)
qry.Parameters("CustID") = CustomerID
Set rs = qry.OpenRecordset(dbOpenForwardOnly, dbReadOnly)
Dim sCodes As String
sCodes = ""
Dim bFirst As Boolean
bFirst = True
Do Until rs.EOF
sCodes = sCodes & IIf(bFirst, "", ",") & rs("Code")
bFirst = False
rs.MoveNext
Loop
rs.Close
qry.Close
GetCodeList = sCodes
End Function
Useful queries of the primary table without duplicate rows will require creating some sort of aggregate queries (i.e. Group By, Count, etc.). Simple selection could be done in a single query, for example
SELECT Customer.CustomerID, Customer.[CustomerName], GetCodeList([CustomerID]) AS Codes, Count(Customer.CustomerID) AS CountOfID
FROM Customer LEFT JOIN [Customer Codes] ON Customer.ID = [Customer Codes].CustomerID
WHERE ((([Customer Codes].Code)="Current" Or ([Customer Codes].Code)="Free"))
GROUP BY Customer.ID, Customer.[CustomerName], GetCodeList([ID]);
More complicated selection may require multiple queries, one to first select the proper records, then another to join the primary table to the first query. But honestly, these types of queries are no more complicated than what can be required to select on Multi-Valued Fields. In fact, the query syntax for MVF is non-standard and can get rather complicated and confusing, even more than having a junction table and many-to-many relationships. Behind the scenes, Access is essentially doing the same thing as I've outlined, but because it hides so much detail it make some queries even more difficult.
Regarding multi-value presentation and selection on a form, mimicking the multi-valued ComboBox exactly is not possible--mainly because the basic Access Combobox does not have a multi-selection option with the ability to show checkboxes. However, one can populate a non-bound ListBox with property [Multi Select] = Simple. On the Form_Load event, add available values (e.g. Code from the example table) to the listbox using ListBox.AddItem method. Then in the Form_Current, Form_AfterUpdate, Form_Undo events, one can add code to show and/or save the selected values. This requires more code which is probably beyond the scope here.
Technically the question asked about "moving" a MVF to another implementation. The gist is to populate the values table (e.g. Code table in the example) with the same values in the MVF list. This could be a manual process, but depends on how the MVF's ComboBox is populated. Then write a query which copies each MFV into the junction table (e.g. [Customer Code]) for the same primary record, something like
INSERT INTO [Customer Codes] ( CustomerID, Code )
SELECT Customer.CustomerID, Customer.TestMVF.Value
FROM Customers
WHERE (((Customers.TestMVF.Value) Is Not Null));
A full implementation is definitely not a simple task overall, but if you find too many problems with MVFs or if you are wanting to migrate to another database, this kind of change will be necessary to understand.

There is no replacement for MVFs in an Access database. Some query techniques can mimic MVFs but you may find MVF functionality to be superior. 1. It is fast and very easy to implement. No code and no SQL. 2. It is visual and therefore it is intuitive for the user. There are some things that you cannot do with an MVF so you really need to decide what is more important.

Related

filter data from one table based on second table without duplicates

Note: I'm working with a large complex database, so for clarity's sake I have simplified the set up below.
I struggled a bit with how to title the question since it's a bit of a complex question. I hope it's accurate enough....
A bit of background first:
I have an Access 2010 database that contains a one-to-many relationship between a table Called "Products" and a table called "Datasets" (ie I have a bunch of products, and each product has multiple datasets). Each has an autonumber key field (p_ID and d_ID respectively) as well as numerous other fields.
I have a form (lets call it frm_Main) that has two subforms: sub_Products (based on the "Products" table) and sub_Datasets (based on the "Datasets" table). On the main form I have a control ctrl_SelectedProduct which is linked to [sub_Products]![p_ID] to see which record in sub_Products is selected / has the focus, and the sub_Datasets subform is linked to this control so that it only displays Datasets records that belong to the selected Products record.
On the main form I also have a number of controls that I'm using for filtering the data in the subforms. For example, I have a control ctrl_Category. I have this control coded so that when the user changes the value of the control, a filter is applied that limits the records in sub_Products to only those that have a p_Category value equal to whatever is chosen in ctrl_Category. If the control is blank, it will show all records. All this works great when if comes to filtering based on fields in the Products table.
Now the problem:
My problem is that I also want to be able to filter based on fields in the Datasets table. For example, Datasets has a field called d_Status. I want the user to be able to chose the status in a control (ctrl_Status), and based on this:
1) sub_Products will be filtered to display only those Products records that have an assotiated Datasets record (or records) that has d_Status equal to whatever was chosen in ctrl_Status
then
2) when a Products record is selected in this filtered sub_Products, sub_Datasets will only show the Datasets records that has d_Status equal to whatever was chosen in ctrl_Status
I know how to do part2. But I have no idea how to get part1 to work. Since sub_Products is currently based on the Products table, which does not have the d_Status field, I have no way to filter it based on that field. Thus, I'm assuming my first step will be to change my setup so that sub_Products is based on a query that combines the two tables (or at least adds the d_Status field).
If I do that, however, I get duplicate Product records. I can't use the query's Unique Values property because if I have a product that has two datasets, one with status "Current" and a one with status "Archived", then those aren't considered duplicates, and the product info is there twice. If I don't have the status field displayed, it shows only unique products initially, but then I can't subsequently apply a filter based on that non-displayed field.
I also tried basing sub_Products on a Totals query in order to utilize the Group By functionality, but I still couldn't get it to work right. Even if I could I don't think that's the best solution, because you can only have 10 fields with Group By, and my Products table has more than 10 fields that I need displayed in sub_Products.
I feel like I'm now just going around in circles and am at a loss of what to try from here. Please help!
Couldn't you add something like this?
Private Sub ctrl_Category_AfterUpdate()
Form_frm_Main.Recordsource = "Select * from Products where p_ID IN(" & _
"Select p_ID from Datasets where d_Status = " & _
Me!ctrl_Category.Value & ")"
End Sub
Alternately, you can access the childform via the parent of the other child form.
This is a non-working snippet just to give you the idea:
Me.Parent.otherChildFormName.Form.Recordsource = whatever

Database design and large tables?

Are tables with lots of columns indicative of bad design? For example say I have the following table that stores user information and user settings:
[Users table]
userId
name
address
somesetting1
...
somesetting50
As the site requires more settings the table gets larger. In my mind this table is normalized, all the settings are dependent on the userId.
I have a thing against tables with lots of columns it just seems wrong to me, but then I remembered that you can select what data to return from the table, so If the table is large I could still break it into several different objects in code. For example
[User object]
[UserSetting object]
and return only the data to fill those objects.
Is the above common practice, or are their other techniques that deal with tables with lots of columns that are more suitable to use?
I think you should use multiple tables like this:
[Users table]
userId
name
address
[Settings table]
settingId
userId
settingKey
settingValue
The tables are related by the userId column which you can use to retrieve the settings for the user you need to.
I would say that it is bad table design. If a user doesn't have an entry for 47 of those 50 settings then you will have a large number of NULL's in the table which isn't good practice and will also slow down performance (NULL's have to be handled in a special way).
Instead, have the following:
USER TABLE
Id,
FirstName
LastName
etc
SETTINGS
Id,
SettingName
USER SETTINGS
Id,
SettingId,
UserId,
SettingValue
You then have a many to many join, and eliminate NULL's
first, don't put spaces in table names! all the [braces] will be a real pain!
if you have 50 columns how meaningful will all that data be for each user? will there be lots of nulls? Most data may not even apply to any given user. Think 1 to 1 tables, where you break down the "settings" into logical groups:
Users: --main table where most values will be stored
userId
name
address
somesetting1 ---please note that I'm using "somesetting1", don't
... --- name the columns like this, use meaningful names!!
somesetting5
UserWidgets --all widget settings for the user
userId
somesetting6
....
somesetting12
UserAccounting --all accounting settings for the user
userId
somesetting13
....
somesetting23
--etc..
you only need to have a Users row for each user, and then a row in each table where that data applies to the given user. I f a user doesn't have any widget settings then no row for that user. You can LEFT join each table as necessary to get all the settings as needed. Usually you only need to work on a sub set of settings based on which part of the application that is running, which means you won't need to join in all of the tables, just the one or tow that you need at that time.
You could consider an attributes table. As long as your indexes are good, then you wouldn't have too much of a performance issue:
[AttributeDef]
AttributeDefId int (primary key)
GroupKey varchar(50)
ItemKey varchar(50)
...
[AttributeVal]
AttributeValId int (primary key)
AttributeDefId int (FK -> AttributeDef.AttributeDefId)
UserId int (probably FK to users table?)
Val varchar(255)
...
basically you're "pivoting" your table with many columns into 2 tables with less columns. You can write views and table functions around this structure to give you data for a group of related items or just a specific item, etc. You could also add other things to the attribute definition table to indicate required data elements, restrictions on the data elements, etc.
What's your thought on this type of design?
Use several tables with matching indexes to get the best SELECT speed. Use the indexes as a way to relate the information between tables using a JOIN.

Merge data object table with associated attributes table in a view

Here's the setup: I have several tables that hold information for data objects which have the potential to have various and sundry bits of data associated with them. Each of these tables has an associated attributes table, which holds 3 bits of information:
the id (integer) of the row the attribute is associated with
a short attribute name ( < 50 chars )
a value (varchar)
The object table will have any number of columns of varying data types, but will always have an integer primary key. If possible, I would like to set up a view that will allow me to select a row from the object table, and all of its associated attributes at one go.
****EDIT****
Ideally, the form I'd like this to take is having columns in the view with the names of the matched attribute from the attributes table, and the value as the value of the attribute.
So for example, if I have table Foo with columns 'Bar', 'Bat', and 'Baz' the view would have those columns, and additionally, columns for any attributes that a row might have.
****END EDIT****
Now, I know (or think I do) that SQL doesn't allow using variables as an alias for a column name. Is there a clean, practical way of doing what I want, or am I chasing a pipe dream?
The obvious solution is to handle all of this in the application code, but I'm curious if it can be done in SQL.
The answer depends on what you are actually seeking. Will the output of the view have one row per attribute per object or one column per attribute per object? If the former, then I'm not sure why you need a view:
Select ...
From ObjectTable
Join AttributeTable
On AttributeTable.Id = ObjectTable.Id
However, I suspect what you want is the later or something like:
Select ...
, ... As Attribute1
, ... As Attribute2
, ... As Attribute3
...
From ObjectTable
In this scenario, the columns that would be generated are not known at execution because the attribute names are dynamic. This is commonly known as a dynamic crosstab. In general, the SQL language is not designed for dynamic column generation. The only way to do this in T-SQL is to use some fugly dynamic SQL. Thus, it is better done in a reporting tool or in middle-tier code.
It sounds like you want a view for each of your 'object' tables as well as its 'attributes' table. Correct me if I am wrong in my reading. It's not clear what your intentions are with 'using variables as an alias for a column name'. Were you hoping to merge ALL your objects, with their different columns, into one view?
Suggest create one view per entity table, and join to its relevant 'attributes' table.
Question though - why is there one matching attributes table for each entity table? Why are they split out? Perhaps you've made the question simpler or obfuscated, so perhaps my question is rhetorical.
CREATE VIEW Foo AS
SELECT O.ID
,O.EverythingElse
,A.ShortName
,A.SomeVarcharValue
FROM
ObjectTable AS O --customer, invoice, whathaveyou
INNER JOIN
ObjectAttribute AS A ON A.ObjectID = O.ID
To consume from this, you could:
SELECT * FROM Foo WHERE ID = 4 OR
SELECT * FROM Foo WHERE ShortName = 'Ender'

What is the best practices in db design when I want to store a value that is either selected from a dropdown list or user-entered?

I am trying to find the best way to design the database in order to allow the following scenario:
The user is presented with a dropdown list of Universities (for example)
The user selects his/her university from the list if it exists
If the university does not exist, he should enter his own university in a text box (sort of like Other: [___________])
how should I design the database to handle such situation given that I might want to sort using the university ID for example (probably only for the built in universities and not the ones entered by users)
thanks!
I just want to make it similar to how Facebook handles this situation. If the user selects his Education (by actually typing in the combobox which is not my concern) and choosing one of the returned values, what would Facebook do?
In my guess, it would insert the UserID and the EducationID in a many-to-many table. Now what if the user is entering is not in the database at all? It is still stored in his profile, but where?
CREATE TABLE university
(
id smallint NOT NULL,
name text,
public smallint,
CONSTRAINT university_pk PRIMARY KEY (id)
);
CREATE TABLE person
(
id smallint NOT NULL,
university smallint,
-- more columns here...
CONSTRAINT person_pk PRIMARY KEY (id),
CONSTRAINT person_university_fk FOREIGN KEY (university)
REFERENCES university (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
);
public is set to 1 for the Unis in the system, and 0 for user-entered-unis.
You could cheat: if you're not worried about the referential integrity of this field (i.e. it's just there to show up in a user's profile and isn't required for strictly enforced business rules), store it as a simple VARCHAR column.
For your dropdown, use a query like:
SELECT DISTINCT(University) FROM Profiles
If you want to filter out typos or one-offs, try:
SELECT University FROM PROFILES
GROUP BY University
HAVING COUNT(University) > 10 -- where 10 is an arbitrary threshold you can tweak
We use this code in one of our databases for storing the trade descriptions of contractor companies; since this is informational only (there's a separate "Category" field for enforcing business rules) it's an acceptable solution.
Keep a flag for the rows entered through user input in the same table as you have your other data points. Then you can sort using the flag.
One way this was solved in a previous company I worked at:
Create two columns in your table:
1) a nullable id of the system-supplied string (stored in a separate table)
2) the user supplied string
Only one of these is populated. A constraint can enforce this (and additionally that at least one of these columns is populated if appropriate).
It should be noted that the problem we were solving with this was a true "Other:" situation. It was a textual description of an item with some preset defaults. Your situation sounds like an actual entity that isn't in the list, s.t. more than one user might want to input the same university.
This isn't a database design issue. It's a UI issue.
The Drop down list of universities is based on rows in a table. That table must have a new row inserted when the user types in a new University to the text box.
If you want to separate the list you provided from the ones added by users, you can have a column in the University table with origin (or provenance) of the data.
I'm not sure if the question is very clear here.
I've done this quite a few times at work and just select between either the drop down list of a text box. If the data is entered in the text box then I first insert into the database and then use IDENTITY to get the unique identifier of that inserted row for further queries.
INSERT INTO MyTable Name VALUES ('myval'); SELECT ##SCOPE_IDENTITY()
This is against MS SQL 2008 though, I'm not sure if the ##SCOPE_IDENTITY() global exists in other versions of SQL, but I'm sure there's equivalents.

How do you manage "pick lists" in a database

I have an application with multiple "pick list" entities, such as used to populate choices of dropdown selection boxes. These entities need to be stored in the database. How do one persist these entities in the database?
Should I create a new table for each pick list? Is there a better solution?
In the past I've created a table that has the Name of the list and the acceptable values, then queried it to display the list. I also include a underlying value, so you can return a display value for the list, and a bound value that may be much uglier (a small int for normalized data, for instance)
CREATE TABLE PickList(
ListName varchar(15),
Value varchar(15),
Display varchar(15),
Primary Key (ListName, Display)
)
You could also add a sortOrder field if you want to manually define the order to display them in.
It depends on various things:
if they are immutable and non relational (think "names of US States") an argument could be made that they should not be in the database at all: after all they are simply formatting of something simpler (like the two character code assigned). This has the added advantage that you don't need a round trip to the db to fetch something that never changes in order to populate the combo box.
You can then use an Enum in code and a constraint in the DB. In case of localized display, so you need a different formatting for each culture, then you can use XML files or other resources to store the literals.
if they are relational (think "states - capitals") I am not very convinced either way... but lately I've been using XML files, database constraints and javascript to populate. It works quite well and it's easy on the DB.
if they are not read-only but rarely change (i.e. typically cannot be changed by the end user but only by some editor or daily batch), then I would still consider the opportunity of not storing them in the DB... it would depend on the particular case.
in other cases, storing in the DB is the way (think of the tags of StackOverflow... they are "lookup" but can also be changed by the end user) -- possibly with some caching if needed. It requires some careful locking, but it would work well enough.
Well, you could do something like this:
PickListContent
IdList IdPick Text
1 1 Apples
1 2 Oranges
1 3 Pears
2 1 Dogs
2 2 Cats
and optionally..
PickList
Id Description
1 Fruit
2 Pets
I've found that creating individual tables is the best idea.
I've been down the road of trying to create one master table of all pick lists and then filtering out based on type. While it works, it has invariably created headaches down the line. For example you may find that something you presumed to be a simple pick list is not so simple and requires an extra field, do you now split this data into an additional table or extend you master list?
From a database perspective, having individual tables makes it much easier to manage your relational integrity and it makes it easier to interpret the data in the database when you're not using the application
We have followed the pattern of a new table for each pick list. For example:
Table FRUIT has columns ID, NAME, and DESCRIPTION.
Values might include:
15000, Apple, Red fruit
15001, Banana, yellow and yummy
...
If you have a need to reference FRUIT in another table, you would call the column FRUIT_ID and reference the ID value of the row in the FRUIT table.
Create one table for lists and one table for list_options.
# Put in the name of the list
insert into lists (id, name) values (1, "Country in North America");
# Put in the values of the list
insert into list_options (id, list_id, value_text) values
(1, 1, "Canada"),
(2, 1, "United States of America"),
(3, 1, "Mexico");
To answer the second question first: yes, I would create a separate table for each pick list in most cases. Especially if they are for completely different types of values (e.g. states and cities). The general table format I use is as follows:
id - identity or UUID field (I actually call the field xxx_id where xxx is the name of the table).
name - display name of the item
display_order - small int of order to display. Default this value to something greater than 1
If you want you could add a separate 'value' field but I just usually use the id field as the select box value.
I generally use a select that orders first by display order, then by name, so you can order something alphabetically while still adding your own exceptions. For example, let's say you have a list of countries that you want in alpha order but have the US first and Canada second you could say "SELECT id, name FROM theTable ORDER BY display_order, name" and set the display_order value for the US as 1, Canada as 2 and all other countries as 9.
You can get fancier, such as having an 'active' flag so you can activate or deactivate options, or setting a 'x_type' field so you can group options, description column for use in tooltips, etc. But the basic table works well for most circumstances.
Two tables. If you try to cram everything into one table then you break normalization (if you care about that). Here are examples:
LIST
---------------
LIST_ID (PK)
NAME
DESCR
LIST_OPTION
----------------------------
LIST_OPTION_ID (PK)
LIST_ID (FK)
OPTION_NAME
OPTION_VALUE
MANUAL_SORT
The list table simply describes a pick list. The list_ option table describes each option in a given list. So your queries will always start with knowing which pick list you'd like to populate (either by name or ID) which you join to the list_ option table to pull all the options. The manual_sort column is there just in case you want to enforce a particular order other than by name or value. (BTW, whenever I try to post the words "list" and "option" connected with an underscore, the preview window goes a little wacky. That's why I put a space there.)
The query would look something like:
select
b.option_name,
b.option_value
from
list a,
list_option b
where
a.name="States"
and
a.list_id = b.list_id
order by
b.manual_sort asc
You'll also want to create an index on list.name if you think you'll ever use it in a where clause. The pk and fk columns will typically automatically be indexed.
And please don't create a new table for each pick list unless you're putting in "relationally relevant" data that will be used elsewhere by the app. You'd be circumventing exactly the relational functionality that a database provides. You'd be better off statically defining pick lists as constants somewhere in a base class or a properties file (your choice on how to model the name-value pair).
Depending on your needs, you can just have an options table that has a list identifier and a list value as the primary key.
select optionDesc from Options where 'MyList' = optionList
You can then extend it with an order column, etc. If you have an ID field, that is how you can reference your answers back... of if it is often changing, you can just copy the answer value to the answer table.
If you don't mind using strings for the actual values, you can simply give each list a different list_id in value and populate a single table with :
item_id: int
list_id: int
text: varchar(50)
Seems easiest unless you need multiple things per list item
We actually created entities to handle simple pick lists. We created a Lookup table, that holds all the available pick lists, and a LookupValue table that contains all the name/value records for the Lookup.
Works great for us when we need it to be simple.
I've done this in two different ways:
1) unique tables per list
2) a master table for the list, with views to give specific ones
I tend to prefer the initial option as it makes updating lists easier (at least in my opinion).
Try turning the question around. Why do you need to pull it from the database? Isn't the data part of your model but you really want to persist it in the database? You could use an OR mapper like linq2sql or nhibernate (assuming you're in the .net world) or depending on the data you could store it manually in a table each - there are situations where it would make good sense to put it all in the same table but do consider this only if you feel it makes really good sense. Normally putting different data in different tables makes it a lot easier to (later) understand what is going on.
There are several approaches here.
1) Create one table per pick list. Each of the tables would have the ID and Name columns; the value that was picked by the user would be stored based on the ID of the item that was selected.
2) Create a single table with all pick lists. Columns: ID; list ID (or list type); Name. When you need to populate a list, do a query "select all items where list ID = ...". Advantage of this approach: really easy to add pick lists; disadvantage: a little more difficult to write group-by style queries (for example, give me the number of records that picked value X".
I personally prefer option 1, it seems "cleaner" to me.
You can use either a separate table for each (my preferred), or a common picklist table that has a type column you can use to filter on from your application. I'm not sure that one has a great benefit over the other generally speaking.
If you have more than 25 or so, organizationally it might be easier to use the single table solution so you don't have several picklist tables cluttering up your database.
Performance might be a hair better using separate tables for each if your lists are very long, but this is probably negligible provided your indexes and such are set up properly.
I like using separate tables so that if something changes in a picklist - it needs and additional attribute for instance - you can change just that picklist table with little effect on the rest of your schema. In the single table solution, you will either have to denormalize your picklist data, pull that picklist out into a separate table, etc. Constraints are also easier to enforce in the separate table solution.
This has served us well:
SQL> desc aux_values;
Name Type
----------------------------------------- ------------
VARIABLE_ID VARCHAR2(20)
VALUE_SEQ NUMBER
DESCRIPTION VARCHAR2(80)
INTEGER_VALUE NUMBER
CHAR_VALUE VARCHAR2(40)
FLOAT_VALUE FLOAT(126)
ACTIVE_FLAG VARCHAR2(1)
The "Variable ID" indicates the kind of data, like "Customer Status" or "Defect Code" or whatever you need. Then you have several entries, each one with the appropriate data type column filled in. So for a status, you'd have several entries with the "CHAR_VALUE" filled in.

Resources