Here''s the text of a quick note I''ve added to the Inheritance wiki page. I''m actually proposing that it would be a good idea to drop automatic single table inheritance and force people to declare it explicitly. There are two reasons for this: 1. Implicitly doing it unnecessarially limits your use of inheritance where you really need it: in your object-oriented programming language. (This is what nailed me--I was doing a simple pull-up-method refactoring and suddenly it started complaining that I had no ''type'' column in my table.) 2. It''s entirely non-relational, and thus bad practice to suggest to those who do not well understand relational DBMSes (which is the vast majority of programmers out there) that this is any sort of a valid technique. SingleTableInheritance is a tool of desperation, when you can''t change your database schema to be relationally sound and make sense. It would in fact be nice if ActiveRecord provided support for class table inheritance. cjs -- Curt Sampson <cjs-gHs2Wiolu3leoWH0uzbU5w@public.gmane.org> +81 90 7737 2974 http://www.NetBSD.org Make up enjoying your city life...produced by BIC CAMERA http://www.rubyonrails.org/show/Inheritance As a note: Single Table Inheritance is extremely unrelational, and if you’re using a DBMS to manage your data integrity, as opposed to a dumb and slow data store, you don’t want to do this. Instead, you want to take the subclass attributes and store them in separate tables related to the main table. Say you’ve got a Contact and a subclass Client with further information. Create a contact table containing all of the contact information, and a client table containing only the information already not in the contact table, with contact_id as the primary key. Now you know that Contact #17 is also a Client if there’s a row for it in the client table; if not, it is not the subclass. This clearly identifies in the database schema what fields are part of the main class, and what fields are part of the subclass. Further, it lets you enforce integrity restrictions on the subclass. For example, if every Client must have a sales group assigned to it, you can set that column to NOT NULL and make it reference the sales_group table, which you can’t do if you put that column in the superclass table. This ensures that you can’t insert invalid Clients into your database. So now that you’ve vowed never to use Single Table Inheritance again, you’re freed up to use inheritance in Ruby for inheriting behaviour, rather than strictly attributes. (For example, I have two types of Active Records?; one connects to one database, one to another. I don’t want to have to add the connection code to every single Active Record subclass I create when I could consolidate it into two abstract classes.) Or you will be once you convince Active Record to ditch the inheritance: class MyActiveRecord << ActiveRecord::Base def self.descends_from_active_record? true end end Or, if you still don''t understand relational databases and insist on using Single Table Inheritance, but still need an abstract class between you and Active Record, you can def self.descends_from_active_record? superclass == MyActiveRecord end
Hi Curt, Without addressing the meat of your post: On Fri, 19 Nov 2004 15:39:13 +0900 (JST), Curt Sampson <cjs-gHs2Wiolu3leoWH0uzbU5w@public.gmane.org> wrote:> > 1. Implicitly doing it unnecessarially limits your use of > inheritance where you really need it: in your object-oriented > programming language. (This is what nailed me--I was doing a simple > pull-up-method refactoring and suddenly it started complaining that > I had no ''type'' column in my table.) >If you want to factor out commonalities, why not just mix-in a module?> cjs > -- > Curt Sampson <cjs-gHs2Wiolu3leoWH0uzbU5w@public.gmane.org> +81 90 7737 2974 http://www.NetBSD.org > Make up enjoying your city life...produced by BIC CAMERASam
Curt & DHH, I 100% agree. Single Table Inheritance is a really bad code/db smell. Not only does it break the rules of good database normalization, it causes class inheritence to behave in unexpected, "magic" ways. On Fri, 19 Nov 2004 15:39:13 +0900 (JST), Curt Sampson <cjs-gHs2Wiolu3leoWH0uzbU5w@public.gmane.org> wrote:> > Here''s the text of a quick note I''ve added to the Inheritance wiki page. > I''m actually proposing that it would be a good idea to drop automatic > single table inheritance and force people to declare it explicitly. > There are two reasons for this: > > 1. Implicitly doing it unnecessarially limits your use of > inheritance where you really need it: in your object-oriented > programming language. (This is what nailed me--I was doing a simple > pull-up-method refactoring and suddenly it started complaining that > I had no ''type'' column in my table.) > > 2. It''s entirely non-relational, and thus bad practice to suggest > to those who do not well understand relational DBMSes (which is the > vast majority of programmers out there) that this is any sort of a > valid technique. SingleTableInheritance is a tool of desperation, > when you can''t change your database schema to be relationally sound > and make sense. > > It would in fact be nice if ActiveRecord provided support for class > table inheritance.-- Regards, John Wilger ----------- Alice came to a fork in the road. "Which road do I take?" she asked. "Where do you want to go?" responded the Cheshire cat. "I don''t know," Alice answered. "Then," said the cat, "it doesn''t matter." - Lewis Carrol, Alice in Wonderland
This one bit me as well when I started using Rails, and while using a mixin module works, it always looks a bit out of place in the otherwise fairly clean model code, and somehow doesn''t feel quite right. That said I like the single table inheritance idea as it seems to work fairly well and is a fairly powerful and unique feature. What would be a wish for me is if the framework created an ''abstract_model'' class (that extends ActiveRecord::Base) in the model directory. All models for that webapp would extend ''abstract_model'' (similar to the way all controllers extend ''abstract_controller''), and would provide a place to for people to put common functionality that all the subclasses can inherit without having to go to the mixin route. Of course the class should be invisible to the single table inheritance model and so that first level subclasses don''t try and write a type column when persisted. Just my 2 cents there. A. -- Aled Davies <awd-bAttSoROYyI@public.gmane.org>> Curt & DHH, > > I 100% agree. Single Table Inheritance is a really bad code/db smell. > > Not only does it break the rules of good database normalization, it > causes class inheritence to behave in unexpected, "magic" ways. > > > On Fri, 19 Nov 2004 15:39:13 +0900 (JST), Curt Sampson <cjs-gHs2Wiolu3leoWH0uzbU5w@public.gmane.org> > wrote: >> >> Here''s the text of a quick note I''ve added to the Inheritance wiki >> page. >> I''m actually proposing that it would be a good idea to drop automatic >> single table inheritance and force people to declare it explicitly. >> There are two reasons for this: >> >> 1. Implicitly doing it unnecessarially limits your use of >> inheritance where you really need it: in your object-oriented >> programming language. (This is what nailed me--I was doing a >> simple >> pull-up-method refactoring and suddenly it started complaining >> that >> I had no ''type'' column in my table.) >> >> 2. It''s entirely non-relational, and thus bad practice to suggest >> to those who do not well understand relational DBMSes (which is >> the >> vast majority of programmers out there) that this is any sort of a >> valid technique. SingleTableInheritance is a tool of desperation, >> when you can''t change your database schema to be relationally >> sound >> and make sense. >> >> It would in fact be nice if ActiveRecord provided support for class >> table inheritance. > > -- > Regards, > John Wilger > > ----------- > Alice came to a fork in the road. "Which road do I take?" she asked. > "Where do you want to go?" responded the Cheshire cat. > "I don''t know," Alice answered. > "Then," said the cat, "it doesn''t matter." > - Lewis Carrol, Alice in Wonderland > _______________________________________________ > Rails mailing list > Rails-1W37MKcQCpIf0INCOvqR/iCwEArCW2h5@public.gmane.org > http://lists.rubyonrails.org/mailman/listinfo/rails >_______________________________________________ Rails mailing list Rails-1W37MKcQCpIf0INCOvqR/iCwEArCW2h5@public.gmane.org http://lists.rubyonrails.org/mailman/listinfo/rails
David Heinemeier Hansson
2004-Nov-19 14:56 UTC
Re: SingleTableInheritance Considered Harmful
> I 100% agree. Single Table Inheritance is a really bad code/db smell.I disagree 98% ;). SIT is an excellent solution to a wide array of inheritance hierarchies that are more alike than different. It''s less well-suited for hierarchies that are more different than alike, but I''ve found that to be the minority of the cases. And in those cases, you can usually factor out the similarities in a stand-alone entity that the different "subclasses" can then associate against. That being said, I''m not categorically against other inheritance models. SIT is just by far the easiest to implement and covers the majority of the cases in my experience.> Not only does it break the rules of good database normalization, it > causes class inheritence to behave in unexpected, "magic" ways.This is a problem with bad error reporting that we should address. It doesn''t have too much to do with SIT as such. -- David Heinemeier Hansson, http://www.basecamphq.com/ -- Web-based Project Management http://www.rubyonrails.org/ -- Web-application framework for Ruby http://macromates.com/ -- TextMate: Code and markup editor (OS X) http://www.loudthinking.com/ -- Broadcasting Brain
I don''t want to claim that I know things like the rules of good database normalisation well but SingleTableInheritance in rails is a wonderful code saver. I use it in many places in an app which was from the ground up planned with rails in mind. Maybe we could change the default and add a new keyword like "persist_class :type" On Fri, 19 Nov 2004 08:57:25 -0500, John Wilger <johnwilger-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:> Curt & DHH, > > I 100% agree. Single Table Inheritance is a really bad code/db smell. > > Not only does it break the rules of good database normalization, it > causes class inheritence to behave in unexpected, "magic" ways. > > > > > On Fri, 19 Nov 2004 15:39:13 +0900 (JST), Curt Sampson <cjs-gHs2Wiolu3leoWH0uzbU5w@public.gmane.org> wrote: > > > > Here''s the text of a quick note I''ve added to the Inheritance wiki page. > > I''m actually proposing that it would be a good idea to drop automatic > > single table inheritance and force people to declare it explicitly. > > There are two reasons for this: > > > > 1. Implicitly doing it unnecessarially limits your use of > > inheritance where you really need it: in your object-oriented > > programming language. (This is what nailed me--I was doing a simple > > pull-up-method refactoring and suddenly it started complaining that > > I had no ''type'' column in my table.) > > > > 2. It''s entirely non-relational, and thus bad practice to suggest > > to those who do not well understand relational DBMSes (which is the > > vast majority of programmers out there) that this is any sort of a > > valid technique. SingleTableInheritance is a tool of desperation, > > when you can''t change your database schema to be relationally sound > > and make sense. > > > > It would in fact be nice if ActiveRecord provided support for class > > table inheritance. > > -- > Regards, > John Wilger > > ----------- > Alice came to a fork in the road. "Which road do I take?" she asked. > "Where do you want to go?" responded the Cheshire cat. > "I don''t know," Alice answered. > "Then," said the cat, "it doesn''t matter." > - Lewis Carrol, Alice in Wonderland > > > _______________________________________________ > Rails mailing list > Rails-1W37MKcQCpIf0INCOvqR/iCwEArCW2h5@public.gmane.org > http://lists.rubyonrails.org/mailman/listinfo/rails >-- Tobi http://blog.leetsoft.com
What would be really nice is an implimentation that works similarly to PostgreSQL''s concept of table inheritence. Lets say I have the table contacts as: * id * lname * fname * address * phone and the table employees as * id * ssn * department * extension * supervisor It would be cool if I could have: class Contact < ActiveRecord::Base ... end class Employee < Contact ... end and then have instances of employee automatically save their lname, fname, address, phone attribs in the contacts table while saving the ssn, department, extension, and supervisor attribs in the employees table. Then, you could either instantiate a Contact (and have access only to the attribs of that class), or an Employee (and have access to all of the attribs of both tables.) With this, you could then also introduce a third table as ''users'' with: * id * username * password and have: class User < Contact ... end Because of the way the inheritance works out, you could have one Contact (with its unique id) that can be _both_ an Employee _and_ a User depending on which class you use to instantiate it. (Obviously, the idea is that ''id'' is the primary key on all three tables, but in ''employees'' and ''users'' it is also a foreign key that references contacts.id.) A solution like this has the advantages of a) not being significantly more complex (for the Rails end-user) than the current SIT implimentation, b) provides better database normalization, c) provides expected class inheritance behavior, and d) opens up a new feature in the ability to instantiate the same base entity in different ways depending on context. DDH, if you''re willing to consider this idea, I''m willing to start working on a prototype/patch-set (and anyone else who would want to contribute is, obviously, welcome). On Fri, 19 Nov 2004 15:56:31 +0100, David Heinemeier Hansson <david-OiTZALl8rpK0mm7Ywyx6yg@public.gmane.org> wrote:> > I 100% agree. Single Table Inheritance is a really bad code/db smell. > > I disagree 98% ;). SIT is an excellent solution to a wide array of > inheritance hierarchies that are more alike than different. It''s less > well-suited for hierarchies that are more different than alike, but > I''ve found that to be the minority of the cases. > > And in those cases, you can usually factor out the similarities in a > stand-alone entity that the different "subclasses" can then associate > against. > > That being said, I''m not categorically against other inheritance > models. SIT is just by far the easiest to implement and covers the > majority of the cases in my experience. > > > Not only does it break the rules of good database normalization, it > > causes class inheritence to behave in unexpected, "magic" ways. > > This is a problem with bad error reporting that we should address. It > doesn''t have too much to do with SIT as such. > -- > David Heinemeier Hansson, > http://www.basecamphq.com/ -- Web-based Project Management > http://www.rubyonrails.org/ -- Web-application framework for Ruby > http://macromates.com/ -- TextMate: Code and markup editor (OS X) > http://www.loudthinking.com/ -- Broadcasting Brain > >-- Regards, John Wilger ----------- Alice came to a fork in the road. "Which road do I take?" she asked. "Where do you want to go?" responded the Cheshire cat. "I don''t know," Alice answered. "Then," said the cat, "it doesn''t matter." - Lewis Carrol, Alice in Wonderland
Thank you for the heads up, This looks indeed interesting but i have a few reservations: * When you say "PostgreSQL''s concept of table inheritence" does that mean it doesn''t work in mysql / sqlite ? * Is this really the way of the least suprise? I hit the type problem myself at one point but the reason why I inherited from an AR class was that i meant to manually do exactly what AR ended up doing automatically! All i needed to do was rename my column kind to type. I think that was about the the same time when I fell in love with AR. Since you posted an example of where your approach is useful, i contribute one from my shop: #################################### class MediaFile < ActiveRecord::Base belongs_to :directory, :class_name => "MediaDirectory", :foreign_key => "directory_id" [...] end class Thumb < MediaFile end class Image < MediaFile end class Logo < MediaFile end class Movie < MediaFile end ########################################## class MediaDirectory < ActiveRecord::Base has_many :media_files, :foreign_key => ''directory_id'' has_many :images, :foreign_key => ''directory_id'' has_many :logos, :foreign_key => ''directory_id'' has_many :thumbs, :foreign_key => ''directory_id'' [....] end Here are some snippets from various views : # product search: <%= media_image @product.directory.thumb.first %> # product page : <%= js_slideshow @product.directory.images %> # product page: <%= media_image @product.vendor.directory.logo.first %> # directory admin: <%= render_partial "mediatable", @directory.media_files %> As you can see, in many different places i need to look up different types of media files, and AR writes the code for me to do that. Maybe in the DB it doesn''t look like Inheritance is going on, but in the code it sure feels that way. -- Tobi http://blog.leetsoft.com
John Wilger wrote:>Curt & DHH, > >I 100% agree. Single Table Inheritance is a really bad code/db smell. > >Not only does it break the rules of good database normalization, it >causes class inheritence to behave in unexpected, "magic" ways. > >Let''s keep in mind that good database normalization may or may not equate to good database design. Normalization is an ideal, but in practical application development, it is not always a good idea. Mapping out inheritance structures in multiple tables can often lead to large numbers of joins that can have a rather detrimental affect on the database.
David Heinemeier Hansson
2004-Nov-20 01:02 UTC
[Rails] SingleTableInheritance Considered Harmful
> Let''s keep in mind that good database normalization may or may not > equate to good database design. Normalization is an ideal, but in > practical application development, it is not always a good idea. > Mapping out inheritance structures in multiple tables can often lead > to large numbers of joins that can have a rather detrimental affect on > the database.I agree. And let it be no secret that I consider the database a necessary evil for the object-oriented domain model. Not the other way around. Hence, when given the choice between implementations that pit OO model against database purity, I will almost always side with the OO model. -- David Heinemeier Hansson, http://www.basecamphq.com/ -- Web-based Project Management http://www.rubyonrails.org/ -- Web-application framework for Ruby http://macromates.com/ -- TextMate: Code and markup editor (OS X) http://www.loudthinking.com/ -- Broadcasting Brain
On Fri, 19 Nov 2004, Sam Stephenson wrote:> If you want to factor out commonalities, why not just mix-in a module?Well, for a start, reducing code duplication. Having a dozen classes inherit from A and mixin B then begs the question of how you can take this pair of statements, put it in a common place, and invoke it with one line. This is done by making B inherit from A and all of the others inherit from B. But more importantly, the most common reason for making new abstract TestCase classes is to consolidate setup and teardown. Invoking a superclass'' setup and teardown is relatively easy and safe. Mixing in several modules that each have their own setup and teardown that must be invoked in the correct order is possible, but painful and complex. cjs -- Curt Sampson <cjs-gHs2Wiolu3leoWH0uzbU5w@public.gmane.org> +81 90 7737 2974 http://www.NetBSD.org Make up enjoying your city life...produced by BIC CAMERA
On Fri, 19 Nov 2004, Thomas Lockney wrote:> >Not only does it break the rules of good database normalization, it > >causes class inheritence to behave in unexpected, "magic" ways. > > Let''s keep in mind that good database normalization may or may not > equate to good database design.It almost invariably does.> Normalization is an ideal, but in practical application development, > it is not always a good idea. Mapping out inheritance structures in > multiple tables can often lead to large numbers of joins that can have > a rather detrimental affect on the database.Sure, but if I took that approach to performance issues, I would encourage the use of assembler over ruby because some code written in ruby might be too slow. While I have no objection, in the practical world, to hacking things in a bad way to get necessary performance, I don''t do it until the problem has actually been demonstrated. And I certainly don''t write frameworks that encourage you to do stuff in a bad way. Encouraging single table inheritance in an RDBMS has a parallel in the ruby world: discourage the use of objects in favour of procedural programming. If I can find the time in the next couple of weeks, I''ll try to write up some sort of paper explaining exactly what the problem is. In the meantime, C.J. Date''s books provide a lot of good information on what a DBMS is. cjs -- Curt Sampson <cjs-gHs2Wiolu3leoWH0uzbU5w@public.gmane.org> +81 90 7737 2974 http://www.NetBSD.org Make up enjoying your city life...produced by BIC CAMERA
On Sat, 20 Nov 2004, David Heinemeier Hansson wrote:> I agree. And let it be no secret that I consider the database a > necessary evil for the object-oriented domain model.A relational database management system (RDBMS) and object-oriented code are quite compatable and complementary. I use them successfully in a tightly integrated way all the time, each for its strengths. All you need to realize this is a good understanding of the relational model and a bit of coding time playing with the two systems. Moving from using a simple data storage tool such as MySQL to using well an RDBMS or something approximating one (PostgreSQL is an open-source example) is right up there with going from procedural to OO programming. Just as many PHP and VB programmers cannot understand why you''d use Java, and many Java programmers cannot understand why you''d use Ruby or Smalltalk, many folks who do not understand the relational model can''t understand why you''d want to bother making a relational database instead of a non-relational one. I would encourage folks to go out and learn about this, and get some of the advantages. For me, it''s cut my overall testing load and reduced the amount of code I write, as well as catching bugs that would have been extremely difficult to find otherwise. cjs -- Curt Sampson <cjs-gHs2Wiolu3leoWH0uzbU5w@public.gmane.org> +81 90 7737 2974 http://www.NetBSD.org Make up enjoying your city life...produced by BIC CAMERA
On Fri, 19 Nov 2004, John Wilger wrote:> What would be really nice is an implimentation that works similarly to > PostgreSQL''s concept of table inheritence.Oh, dear, I hope not *too* similarly. PostgreSQL''s method of table inheritance is a left-over failed experiment from the days when Postgres was a research project. It''s not relational and is severely broken. Why it''s not been deleted I have no idea. The biggest problem with it is that it silently breaks constraints in other tables. Take your example, slightly simplified to save space: CREATE TABLE contact (id int PRIMARY KEY, name text, phone text) INSERT INTO contact VALUES (1, ''me'', ''1234'') INSERT INTO contact VALUES (2, ''him'', ''5678'') Now, I have some contacts that are also employees: CREATE TABLE employee (ssn text) INHERITS (contact) I can create a contact that is also an employee: INSERT INTO employee VALUES (3, ''someone'', ''5555'', ''123-45-6789'') All''s happy, right? But then you try this: INSERT INTO employee VALUES (1, ''someone else'', ''4444'', ''888-77-6666'') Oh no! It accepted it! What''s going on? SELECT * FROM contact WHERE id = 1 produces id | name | phone ----+--------------+------- 1 | me | 1234 1 | someone else | 4444 (2 rows) I have two different records with the same primary key. My database integrity is completely screwed at this point. This is why you should NEVER use inheritance in postgres; it breaks the promise that a primary key is unique. (That, BTW, is a critical part of the relational model: any database where every tuple in every relation cannot be identified uniquely by at least one candidate key is no longer relational.) Beyond this, there are also issues such as how you change a contact into an employee and vice versa. The truly stupid thing about all this is that the relational model already handles this case just fine anyway: CREATE TABLE contact (id int PRIMARY KEY, name text, phone text) CREATE TABLE employee (id int PRIMARY KEY REFERENCES contact, ssn text) Some people are going to talk about performance and multiple joins and whatnot at this point, but I find that a fairly specious argument. If there''s a particular case where you''ve measured the situation and found it wanting, and doing some ugly kluge like denormalizing your database is necessary to fix it, sure, go ahead. But we all know by now that the first rule for dealing with performance issues is to do it right, measure it, and then figure out if you need to change things. Often you won''t. Often, when you do, you''ll find the performance problem is not what you thought it was. If we didn''t all of us here believe that premature optimization is bad, or we''d still be using assembler instead of C, and C instead of Ruby. Ok, that rant aside, let''s look at this:> It would be cool if I could have: > > class Contact < ActiveRecord::Base; ...; end > class Employee < Contact; ...; end > > and then have instances of employee automatically save their lname, > fname, address, phone attribs in the contacts table while saving the > ssn, department, extension, and supervisor attribs in the employees > table.This I do agree with, since this is basically doing things the relational way I pointed out above--Class Table Inheritance, I belive the pattern is called. However, even given that this is doing stuff the "correct" way (relationally), I''m still not sure I want it to be the default, rather than explicitly declared. As I mentioned in my intial post, I got socked in the side of the head because I was doing a standard refactoring (pull up method) that involved creating a new class, and suddenly magic started happening. Magic is good when it always (or at least almost always) does the right thing. Magic is not good when there are three different documented table inheritance patterns (Concrete Table Inheritance has yet to be mentioned explicitly, though in fact that''s just what PostgreSQL''s table inheritance is) as well as the possibility that you didn''t want to use any kind of table inheritance at all.> Because of the way the inheritance works out, you could have one > Contact (with its unique id) that can be _both_ an Employee _and_ a > User depending on which class you use to instantiate it.Yes. Which is interesting. And in fact is probably the more common case, though I find it fantastically irritating that current modern DBMSes, outside of maybe DB2, don''t provide the type of cross-table constraints you need to be able to say that a Parent can be a Child_A or a Child_B but not both.> DDH, if you''re willing to consider this idea, I''m willing to start > working on a prototype/patch-set (and anyone else who would want to > contribute is, obviously, welcome).I would recommend going ahead full speed for reasons: 1. It is, relationally, a better way of mapping out these sort of data. 2. It would get another technique in there, which would show that, given that there''s more than one pattern, we should ask programmers to make a conscious choice rather than just use what''s there because they don''t know better and it seems to be the standard.> On Fri, 19 Nov 2004 15:56:31 +0100, David Heinemeier Hansson > <david-OiTZALl8rpK0mm7Ywyx6yg@public.gmane.org> wrote: > > > > I disagree 98% ;). SIT is an excellent solution to a wide array of > > inheritance hierarchies that are more alike than different.As for this, it depends on what you consider an excellent solution. If you *never* access that database *in any way* but through that particular ruby code, it seems all right. However, keep in mind you''ve now got data validation logic in that ruby code, and you''re going to have to duplicate that should anything else ever write that database. The obvious refactoring is to move the validation to the database so that everything writing that table can (and is forced to!) take advantage of it. You may even get some issues with things only reading the database, since the schema is, when you use STI rather than CTI, no longer clearly communicating intent.> > > Not only does it break the rules of good database normalization, it > > > causes class inheritence to behave in unexpected, "magic" ways. > > > > This is a problem with bad error reporting that we should address. It > > doesn''t have too much to do with SIT as such.I entirely disagree. One of the most basic, well-known and (most importantly) common refactorings ought not blow up in one''s face like that. cjs -- Curt Sampson <cjs-gHs2Wiolu3leoWH0uzbU5w@public.gmane.org> +81 90 7737 2974 http://www.NetBSD.org Make up enjoying your city life...produced by BIC CAMERA
On Mon, 22 Nov 2004 00:08:38 +0900 (JST), Curt Sampson <cjs-gHs2Wiolu3leoWH0uzbU5w@public.gmane.org> wrote:> On Fri, 19 Nov 2004, John Wilger wrote: > > > What would be really nice is an implimentation that works similarly to > > PostgreSQL''s concept of table inheritence. > > Oh, dear, I hope not *too* similarly. PostgreSQL''s method of table > inheritance is a left-over failed experiment from the days when Postgres > was a research project. It''s not relational and is severely broken. Why > it''s not been deleted I have no idea.No, I certainly don''t mean "too similarly". I''m well aware of the implementation problems that PostgreSQL has in this area (it''s bit me in the arse once or twice). However, the "idea" of table inheritance isn''t bad, it''s simply the implementation (in Postgres) that stinks. We could certainly provide better behavior, and we''d have to do so through the code anyway, so that other DBMS would be supported.> Some people are going to talk about performance and multiple joins and > whatnot at this point, but I find that a fairly specious argument. If > there''s a particular case where you''ve measured the situation and found > it wanting, and doing some ugly kluge like denormalizing your database > is necessary to fix it, sure, go ahead. But we all know by now that > the first rule for dealing with performance issues is to do it right, > measure it, and then figure out if you need to change things. Often > you won''t. Often, when you do, you''ll find the performance problem is > not what you thought it was. If we didn''t all of us here believe that > premature optimization is bad, or we''d still be using assembler instead > of C, and C instead of Ruby.Agreed. Besides, anymore the rule of thumb is generally that adding more/better hardware tends to be a _lot_ cheaper these days than paying programmers to optimize code. Would you rather spend $500 extra on hardware, or pay a programmer $100/hr to put 16 hours into optimizing the speed of the code? When deciding whether to optimize the code, you really have to way the two options. (Sometimes code optimization _is_ cheaper---if you are going to be running the same code on a lot of installations, for instance. In that case, the programming only has to be done once, but the extra hardware is needed for every installation.)> > Ok, that rant aside, let''s look at this: > > > It would be cool if I could have: > > > > class Contact < ActiveRecord::Base; ...; end > > class Employee < Contact; ...; end > > > > and then have instances of employee automatically save their lname, > > fname, address, phone attribs in the contacts table while saving the > > ssn, department, extension, and supervisor attribs in the employees > > table. > > This I do agree with, since this is basically doing things the > relational way I pointed out above--Class Table Inheritance, I belive > the pattern is called. > > However, even given that this is doing stuff the "correct" way > (relationally), I''m still not sure I want it to be the default, rather > than explicitly declared. As I mentioned in my intial post, I got socked > in the side of the head because I was doing a standard refactoring > (pull up method) that involved creating a new class, and suddenly > magic started happening. Magic is good when it always (or at least > almost always) does the right thing. Magic is not good when there are > three different documented table inheritance patterns (Concrete Table > Inheritance has yet to be mentioned explicitly, though in fact that''s > just what PostgreSQL''s table inheritance is) as well as the possibility > that you didn''t want to use any kind of table inheritance at all.I can certainly see your point here. Perhaps the default should be no inheritance of the table from class to class unless specifically requested. You might be able to have a couple of macros in the AR class definitions such as: class Contact < ActiveRecord::Base ... end class Employee < Contact using_cti :contact ... end or class User < Contact using_sti :contact ... end Maybe not that specific grammar, but it illustrates the point. Employee would use an ''employees'' table for it''s info, but it would save any attributes that matched the attributes of Contact in the ''contacts'' table. User would use the existing single-table inheritance model to store its data in the ''contacts'' table. Then, if you had: class Person < Contact ... end Person would inherit all of the methods from Contact, but it would store all of its information solely in the ''people'' table, since no table inheritance method was specified. -- Regards, John Wilger ----------- Alice came to a fork in the road. "Which road do I take?" she asked. "Where do you want to go?" responded the Cheshire cat. "I don''t know," Alice answered. "Then," said the cat, "it doesn''t matter." - Lewis Carrol, Alice in Wonderland
On Sun, 21 Nov 2004, John Wilger wrote:> Agreed. Besides, anymore the rule of thumb is generally that adding > more/better hardware tends to be a _lot_ cheaper these days than > paying programmers to optimize code.Heck: it''s not even that. There''s no reason that most DBMSs, if given a hint to, could not allocate cluster corresponding rows from multiple logical tables (contact, customer, employer, etc., where all share a common contact_id) together on disk, so that a join of three rows from contact, customer and employer would acctually read only one physical row from disk. But people are so used to mucking with their logical schema in order to try to optimize physical storage that nobody''s doing stuff like this. Ok. I''ll try to stop ranting now. :-)> I can certainly see your point here. Perhaps the default should be no > inheritance of the table from class to class unless specifically > requested.Exactly what I''d like to see. I don''t think that you need much syntax for single table inheritance: just putting in class Contact < ActiveRecord::Base use_single_table_inheritance end or class Contact < ActiveRecord::Base use_single_table_inheritance :column => ''subclass'' end is fine. Subclasses need specify nothing, as far as I can see, since everything just works for them; the only time you need to know you''re using STI is on a load via the superclass where you have to use the type column to decide what to instantiate. The fields are the same in all objects, so you can never have a problem there; the only thing a subclass changes is behaviour embedded in ruby code. I think that the same would work for class table inheritance: class Contact < ActiveRecord::Base use_class_table_inheritance end Here the subclasses already know that they use class table inheritance, because they inherit that attribute from their parent. And then it could automatically do the appropriate load/store stuff with the joins, which would be really cool. The only real issue there is dealing with transactions: when you create a new child class with CTI, you want the inserts into the parent and child tables to succeed or fail as a pair, which means they have to happen within a single transaction. That means starting and committing one if you''re in auto-commit mode. But that shouldn''t be too hard. cjs -- Curt Sampson <cjs-gHs2Wiolu3leoWH0uzbU5w@public.gmane.org> +81 90 7737 2974 http://www.NetBSD.org Make up enjoying your city life...produced by BIC CAMERA
I can''t say I understand what you are discussing enough to feel confident to interrupt but while looking arround searching for more info I found this: http://www.agiledata.org/essays/mappingObjects.html#MapToGenericStructure Could a Generic Schema help? The idea of being able to specify in my code what needs to persist without having to worry about the database schema sounds interesting. But then again, I''m just getting started on the subject.
On Mon, 22 Nov 2004, [ISO-8859-1] "Luis G. Gmez" wrote:> http://www.agiledata.org/essays/mappingObjects.html#MapToGenericStructure > Could a Generic Schema help?>From a relational point of view, that''s even worse. Now your DBMS iseven doing less checking on the data than it was before. I wouldn''t go so far as to say you never want to move data integrity checking out of the RDBMS and into your application. A lot of people use MySQL which, being more glorified flat file storage than a DBMS, can''t do much checking anyway. In other cases, perhaps, no other application will ever access the database and for whatever reason it''s easer to do certain constraints in code. However, if you start doing serious consistency checking in the RDBMS, you can save yourself a lot of code. The database itself will catch bugs in your insert and update code, because it won''t let the code put stuff into an invalid or inconsistent state. I find that this lets me write fewer unit tests. As well, when you''re loading informatin from the database you save yourself the bother of checking it and, more importantly, save yourself the bother of figuring out what to do if the data are inconsistent. ("Oh dear, we have three order lines without an order. What''s now?") The RDBMS is there to help you, and if you know how work with it, it will help you a lot. If it seems to be putting blocks between you and what you want to do, look a litte more closely: maybe it''s trying to tell you that you have a logical inconsistency somewhere in what you''re trying to do. cjs -- Curt Sampson <cjs-gHs2Wiolu3leoWH0uzbU5w@public.gmane.org> +81 90 7737 2974 http://www.NetBSD.org Make up enjoying your city life...produced by BIC CAMERA
On Mon, 22 Nov 2004, [ISO-8859-1] "Luis G. Gmez" wrote:> http://www.agiledata.org/essays/mappingObjects.html#MapToGenericStructureOh, one more thing about this: if you''re using this, you should be seriously asking yourself why you want to go to all this work instead of just serializing your objects directly into a single database field. Then you just need an ''(oid, blob)'' table which will leave you with a lot less code. cjs -- Curt Sampson <cjs-gHs2Wiolu3leoWH0uzbU5w@public.gmane.org> +81 90 7737 2974 http://www.NetBSD.org Make up enjoying your city life...produced by BIC CAMERA