Template Tables

This is an SqlPattern.


Intent Create small template tables, that aid you to build the needed result set using SQL. These tables are often included in the from clause with CartesianProduct and participate in complex additional restrictions in the where clause.


Context You try to build a nontrivial resultset, involving relationships between records 'near' to each other, or need to generate rows that are not in the database from the exisiting ones using some rules. You seem to need a variable number of SelfJoins?.


Forces


Solution

Create a small additional table (most often containing a single column with some integer numbers) and produce the CartesianProduct with your table. Use the result to compute the needed fields and filter/group as necessary (ThinkSqlAsConstraintSatisfaction).


Resulting context

The additional columns you get help to make the fields you need in the result. Having several copies of a row from the original table in the result set makes possible grouping of every row with its near ones (the predcessors and/or successors in an ordering), or to 'generate' new rows from a single existing row.


Example

For example, in order to calculate WeightedMovingAverage? over the table data_sequence:

 Idx        Value
 ----------------------
 ....
 45         234.5
 46         245.8
 47         251.5
 48         250.3
 49         231.0
 50         252.6
 .....

you create table moving_average_mask:

 Seq       Weight
 -----------------------
 -1        0.25
  0        0.5
  1        0.25

and execute SQL statement:

 select (moving_average_mask.Seq + data_sequence.Idx), sum(data_sequence.Value * moving_average_mask.Weight)
 from data_sequence, moving_average_mask
 group by (moving_average_mask.Seq + data_sequence.Idx)
 having sum(moving_average_mask.Weight)=1

The result of the CartesianProduct is every row from data_sequence replicated three times, each copy paired with a row from moving_average_mask:

 Idx        Value      Seq       Weight
 ------------------------------------------
 ...
 46         245.8       -1         0.25
 46         245.8        0         0.5
 46         245.8        1         0.25
 47         251.5       -1         0.25
 47         251.5        0         0.5
 47         251.5        1         0.25
 48         250.3       -1         0.25
 48         250.3        0         0.5
 48         250.3        1         0.25
 ...

Every row is grouped with its neighbours as defined by Idx (usually this is a date column):

 Idx        Value      Seq       Weight
 ------------------------------------------
 ...
 46         245.8       -1         0.25
 47         251.5        0         0.5
 48         250.3        1         0.25
 ...

Then the average is easy to calculate. Having clause strips both ends that do not have preceding or succeeding entries in their groups.


Another example is if you have price quotes in time, and want to use a quote for day T for day T-1, T-2, up to T-n if you miss the quotes for these days in the database. You create a table with integers 0..n, make CartesianProduct, use dateadd(T_column,num) instead of T_column and filter out the extra rows.

-- NikolaToshev

I have this pattern. I rediscover it every couple of years. Thanks for writing it down.


EditText of this page (last edited October 2, 2001) or FindPage with title or text search