Here is a proc that will spit out a table like the one you want:
proc_doc util_table_from_list { title list { n_columns 1 } { width 100% } } {
returns html for a table, with each item in $list in its own tr/td
} {
set tr_txt ""
set i 0
foreach item $list {
if { [expr "$i % $n_columns"] == 0 } {
if { $i > 0 } {
append tr_txt </tr>
}
append tr_txt <tr>
}
append tr_txt "
<td valign=top><img src=/graphics/bullet.gif></td>
<td valign=top> $item</td>"
}
if { $i > 0 } {
append tr_txt </tr>
}
set n_colspan [expr "$n_columns + 1"]
return "
<table cellspacing=0 cellpadding=0 width=$width>
<tr><td colspan=$n_colspan align=center>$title</td></tr>
$tr_txt
</table>"
}
Second I would comment that your proc makes between 2 and 8 calls to the db every time it is called. If you were using a normalized data model it should take at most 2 and probably only 1. ec_product_purchase_comb should probably look like
create table ec_product_purchases (
product_id_1 int not null references ec_products(product_id),
product_id_2 int not null references ec_products(product_id)
);
you can use the postgres limit keyword if you are concerned about getting more than 6 entries, and if down the road you find you need more than 6 it will be trivial to allow that. I.e. just select out the purchase_id_2 entries matching purchase_id_1 and loop through those rather than the two-step process you have where you have six different product_$counter entries. (Also this way if you have a row in there for every product pair a user purchases you can also do an order by on how popular that matching is.)