# De-normalized table associations

**URL:** <https://www.ruby-forum.com/t/de-normalized-table-associations/184010>\
**Category:** Rails\
**Created:** [February 23, 2010, 8:33pm UTC](https://www.ruby-forum.com/t/de-normalized-table-associations/184010 "2010-02-23T20:33:49Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![pepe](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/p/4da419/32.png) [@pepe](https://www.ruby-forum.com/u/pepe)\
**Post date:** [February 23, 2010, 8:33pm UTC](https://www.ruby-forum.com/t/de-normalized-table-associations/184010/1 "2010-02-23T20:33:49Z")

</div>

Hi,

Working with legacy DB here…

2 tables:  
Projects  
Workers

A worker record has many fields in it to hold project codes as in:  
project1, project2, project3, … project30 (great, isn’t it?..)

Normalizing the DB is out of the question.

I’ve been thinking about this for a while and I can’t find a way to  
associate both tables as in:

“Project has\_many :workers”  
“Worker.project1 belongs\_to :project”  
“Worker.project2 belongs\_to :project”  
etc.

Any ideas?

Pepe

---

<div class="post-metadata">

**Author:** ![pepe](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/p/4da419/32.png) [@pepe](https://www.ruby-forum.com/u/pepe)\
**Post date:** [February 23, 2010, 8:42pm UTC](https://www.ruby-forum.com/t/de-normalized-table-associations/184010/2 "2010-02-23T20:42:36Z")

</div>

you need has\_and\_belongs\_to\_many  
[http://railsbrain.com/api/rails-2.3.2/doc/index.html?a=M001888&name=has\_and\_belongs\_to\_many](http://railsbrain.com/api/rails-2.3.2/doc/index.html?a=M001888&name=has_and_belongs_to_many)

---

<div class="post-metadata">

**Author:** ![pepe](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/p/4da419/32.png) [@pepe](https://www.ruby-forum.com/u/pepe)\
**Post date:** [February 23, 2010, 8:48pm UTC](https://www.ruby-forum.com/t/de-normalized-table-associations/184010/3 "2010-02-23T20:48:28Z")

</div>

Thanks Ben, but I don’t believe so. As I said in the OP, the table is  
‘de-normalized’. I don’t have 1 record per worker and project to be  
able to associate the tables the usual way. I have many fields holding  
project code values in the same record, obviously all of them with  
different names. In other words, I have many fields to “hook into” for  
the association to happen, not just one neat field I can associate  
with.

---

<div class="post-metadata">

**Author:** ![pepe](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/p/4da419/32.png) [@pepe](https://www.ruby-forum.com/u/pepe)\
**Post date:** [February 23, 2010, 8:54pm UTC](https://www.ruby-forum.com/t/de-normalized-table-associations/184010/4 "2010-02-23T20:54:55Z")

</div>

On 23 February 2010 19:40, ben wiseley [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> you need  
> has\_and\_belongs\_to\_many [http://railsbrain.com/api/rails-2.3.2/doc/index.html?a=M001888&name=has\_and\_belongs\_to\_many](http://railsbrain.com/api/rails-2.3.2/doc/index.html?a=M001888&name=has_and_belongs_to_many)

How, precisely will a join-table help, when he says that “Normalizing  
the DB is out of the question”?  
:-/

On 23 February 2010 19:32, pepe [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> Normalizing the DB is out of the question.
> 
> I’ve been thinking about this for a while and I can’t find a way to  
> associate both tables as in:
> 
> “Project has\_many :workers”  
> “Worker.project1 belongs\_to :project”  
> “Worker.project2 belongs\_to :project”  
> etc.

You are highly limited given your inability to re-structure the  
database, and I wonder whether you want to reconsider negotiation with  
whatever powers that be to refactor the database a little (or a lot!  
😉

But working with the tables you have, you can at least use AR as much  
as possible…

# worker model

belongs\_to :project1, :class\_name =\> “Project”, :foreign\_key =\>  
“project1\_foreign\_key\_name\_in\_workers\_table”  
belongs\_to :project2, :class\_name =\> “Project”, :foreign\_key =\>  
“project2\_foreign\_key\_name\_in\_workers\_table”  
belongs\_to :project3, :class\_name =\> “Project”, :foreign\_key =\>  
“project3\_foreign\_key\_name\_in\_workers\_table”

The project model is going to be harder, and you’re not going to be  
able to intuitively assign workers to projects, but you can at least  
fudge-together some helper methods to make it look like you’re  
leveraging the power of Rails! 🙂

#project model  
has\_many :project1\_keyed\_workers, :class\_name =\> “Worker”,  
:foreign\_key =\> “project1\_foreign\_key\_name\_in\_workers\_table”  
has\_many :project2\_keyed\_workers, :class\_name =\> “Worker”,  
:foreign\_key =\> “project2\_foreign\_key\_name\_in\_workers\_table”  
has\_many :project3\_keyed\_workers, :class\_name =\> “Worker”,  
:foreign\_key =\> “project3\_foreign\_key\_name\_in\_workers\_table”

def workers

# build and array of all the arrays of workers, flatten it and get

rid of nil values  
([] \<\< project1\_keyed\_workers \<\< project2\_keyed\_workers \<\<  
project3\_keyed\_workers).flatten.compact  
end

Hope this helps…  
Michael

---

<div class="post-metadata">

**Author:** ![pepe](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/p/4da419/32.png) [@pepe](https://www.ruby-forum.com/u/pepe)\
**Post date:** [February 23, 2010, 8:57pm UTC](https://www.ruby-forum.com/t/de-normalized-table-associations/184010/5 "2010-02-23T20:57:50Z")

</div>

On 23 February 2010 19:54, Michael P. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> whoa! that doesn’t look very legible once posted.  
> If you can’t make sense of my code snippet, let me know and I’ll post  
> a Gist link.

In fact, I might as well do that anyway:

> <https://gist.github.com/Pavling/312634>

---

<div class="post-metadata">

**Author:** ![pepe](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/p/4da419/32.png) [@pepe](https://www.ruby-forum.com/u/pepe)\
**Post date:** [February 23, 2010, 9:21pm UTC](https://www.ruby-forum.com/t/de-normalized-table-associations/184010/6 "2010-02-23T21:21:51Z")

</div>

Thanks a lot Michael for your explanation and efforts. 🙂

I had thought about going the way you propose but it seemed to me like  
too much work (I think the total amount of columns I would need to  
write code for is 32) for what I actually need. If there is an easy  
way of making the association work both ways I surely would like to  
know, however, while you were trying to help me I think I found a neat  
solution for my current need, which is not having the association work  
both ways but accessing the project information for a given worker.

The problem I’m trying to solve is a little bit more complicated than  
I originally posted. I am not really dealing with ID values. The  
Projects table has an ID and also a project code, which is also  
supposed to be unique “when it has a value” (this is enforced at  
application level). The project ID is not what is stored in the  
‘project’ columns in the Worker record, it is the project code  
instead.

The solution I have so far works quite well and uses virtual  
attirbutes (below). The first virtual attribute is a list of all  
project codes actually stored in the record. If a ‘project\*’ column is  
empty it does not provide a value to the list. The second one is the  
projects information based on those project codes the first virtual  
attribute provides.

class Worker \< ActiveRecord::Base  
attr\_reader :project\_codes, :projects

def project\_codes  
self.attributes.delete\_if {|key, value| key.downcase !~ /project/  
|| value.blank? }.values.map{|v| v.strip}.uniq  
end

def projects  
Project.find\_all\_by\_code self.project\_codes  
end  
end

---

<div class="post-metadata">

**Author:** ![pepe](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/p/4da419/32.png) [@pepe](https://www.ruby-forum.com/u/pepe)\
**Post date:** [February 23, 2010, 8:56pm UTC](https://www.ruby-forum.com/t/de-normalized-table-associations/184010/7 "2010-02-23T20:56:05Z")

</div>

On 23 February 2010 19:52, Michael P. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> has\_many :project1\_keyed\_workers, :class\_name =\> “Worker”,  
> :foreign\_key =\> “project1\_foreign\_key\_name\_in\_workers\_table”  
> has\_many :project2\_keyed\_workers, :class\_name =\> “Worker”,  
> :foreign\_key =\> “project2\_foreign\_key\_name\_in\_workers\_table”  
> has\_many :project3\_keyed\_workers, :class\_name =\> “Worker”,  
> :foreign\_key =\> “project3\_foreign\_key\_name\_in\_workers\_table”

whoa! that doesn’t look very legible once posted.  
If you can’t make sense of my code snippet, let me know and I’ll post  
a Gist link.

---

<div class="post-metadata">

**Author:** ![pepe](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/p/4da419/32.png) [@pepe](https://www.ruby-forum.com/u/pepe)\
**Post date:** [February 23, 2010, 10:06pm UTC](https://www.ruby-forum.com/t/de-normalized-table-associations/184010/8 "2010-02-23T22:06:21Z")

</div>

Michael,

If it were me who had created that monstrosity I would have apologized  
in advance, believe me. 🙂

Funny enough, I might very probably be on the phone talking to the  
“owner of the beast” in a few minutes and believe it or not, instead  
of starting to keep things ‘sane’ and add additional information in  
new tables he has already suggested adding a brand new set of 32  
columns for the related values we need to store now! 😃

The sad thing is that the way things are now we might just need to put  
up with it! This guy is obviously on his way out and the application  
will be rewritten and the DB redesigned at some point in the near  
future. The owner of the application is trying to find the “path of  
least resistance” right now to make things work so we can meet the  
deadlines we have and start working on the replacement software asap  
so we don’t need have this other person involved any longer.

I’ll remember to bring my Louisville Slugger if I ever meet him in  
person. 😉

---

<div class="post-metadata">

**Author:** ![pepe](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/p/4da419/32.png) [@pepe](https://www.ruby-forum.com/u/pepe)\
**Post date:** [February 23, 2010, 9:40pm UTC](https://www.ruby-forum.com/t/de-normalized-table-associations/184010/9 "2010-02-23T21:40:35Z")

</div>

On 23 February 2010 20:20, pepe [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> instead.  
> Well, the first thing you have to do is creep up behind the original  
> developer of that table with your Louisville Slugger in hand…

But seriously, whoever produced that table should be fired. If my  
plumber connected the taps that badly I’d get wet every time I change  
channel on the TV. [1]

> If there is an easy  
> way of making the association work both ways I surely would like to  
> know

Given your table structure, I think you’ve done well to get as much  
use out of it as you have; your “projects” method is another little  
fudge I might have considered.  
You might get something else working (both ways with a little hassle)  
using named scopes rather than associations; defining them (all 32+!)  
in a loop to be DRY, and concatenating their results like I suggested  
with the associations. But it’s a lot of hard work, and not really  
what Rails is best at.

Honestly, I think you’d be better off spending the time scouring the  
job sites for a new employer; somewhere where databases are free to  
enjoy the benefits of friendly ORMs 🙂

Good luck,  
Michael

[1] If it was _you_ that created the current tables, but “years ago,  
before you knew what you were doing”… well, it’s fair enough that  
you’re trying to sort it out now… as long as you’re suitably  
apologetic 😉
