views:

87

answers:

2

Due to a variety of design decisions, we have a table, 'CustomerVariable'. CustomerVariable has three bits of information--its own id, an id to Variable (a list of possible settings the customer can have), and the value for that variable. The Variable table, on the other hand, has the information on a default--in case the CustomerVariable is not set.

This works well in most situations, allowing us not to have to create an insanely long list of information--especially in a case where there are 16 similar variables that need to be handled for a customer.

The problem comes in trying to get this information into a select. So far, our 'best' solution involves far too many joins to be efficient--we get a list of the 16 VariableIds we need information on, setting them into variables, first. Later on, however, we have to do this:

   CROSS JOIN dbo.Variable v01
   LEFT JOIN dbo.CustomerVariable cv01 ON cv01.customerId = c.id 
                                      AND cv01.variableId = v01.id
   CROSS JOIN dbo.Variable v02
   LEFT JOIN dbo.CustomerVariable cv02 ON cv02.customerId = c.id 
                                      AND cv02.variableId = v02.id
   -- snip --
   CROSS JOIN dbo.Variable v16
   LEFT JOIN dbo.CustomerVariable cv16 ON cv16.customerId = c.id 
                                      AND cv16.variableId = v16.id
WHERE
   v01.id = @cv01VariableId
   v02.id = @cv02VariableId
   -- snip --
   v16.id = @cv16VariableId

I know there has to be a better way, but we can't seem to find it amidst crunch time. Any help would be greatly appreciated.

+3  A: 

If your data set is relatively small and not too volatile, you may want to use materialized views (assuming your database supports them) to optimize the lookup.

If materialized views are not an option, consider writing a stored procedure that retrieves that data in two passes:

  1. First retrieve all of the CustomerVariables available for a particular customer (or set of customers)
  2. Next, retrieve all of the default values from the Variables table
  3. Perform a non-distinct union on the results merging the defaults in wherever a CustomerVariable record is missing.

Essentially, this is the equivalent of:

SELECT variableId, 
       CASE WHEN CV.variableId = NULL THEN VR.defaultValue ELSE CV.value END
FROM Variable VR
LEFT JOIN CUstomerVariable CV on CV.variableId = VR.variableId
WHERE CV.customerId = c.id
LBushkin
Incidentally, I've gone both routes (mine described above, and this one). There are good things and bad things to both. With the two pass method, you can actually respond faster to the browser, and easily terminate the loop to get the data if the user so chooses; however, the total query time is slower. With the view route, it's easier to give direct query access for other applications.
Chris Lively
+1  A: 

The type of query you want is called a pivot table or crosstab query.

By far the easiest way of dealing with this is to create a view based off of a crosstab query. This will flip the columns from being vertical to being horizontal like a regular sql table. Once that is done, just query the view. Easy. ;)

Chris Lively