brandonk — 2010-09-21T13:00:01-04:00 — #1
I have a basic 1:Many table relationship for products and the categories they are in. The tables are set up as
This has been in place for years, works great, yadda yadda. Now we have a new requirement where we need to specify a single relationship (inside of CxP) as the "master category". Currently when we want to know about the product, we select the categories and only use the first returned category. This obviously doesn't always work well or even consistently since there is nothing to sort CxP on.
Without a complete overhaul of the current structure, what is the best way to accomplish this? I would like to be able to enforce the 1 master per product at the database level, but the only structure I can think of that allows that is a column that is "is_master" where no is NULL and yes is 1.... but that's a nasty hack
brandonk — 2010-09-23T10:44:47-04:00 — #2
Rudy, does that smiley mean my schema needs an overhaul or was there something else I am missing?
Guido, yeah, I can handle it in the app logic, I just like to have the database enforce relationship restraints when possible (it gives me more peace of mind). So far Rudy's recommendation is winning (I like the idea of flexibility for other category markers in the future).
guido2004 — 2010-09-23T01:44:19-04:00 — #3
You can also handle the master category assignment in your application code: when someone assigns a master category, check if there already is one, and react any way you want: give a message, change the master category, whatever.
r937 — 2010-09-23T12:52:53-04:00 — #4
it means that i see that you are on the right track and i am happy about it
the many-to-many table is a necessity
choosing one of the many to be a "first among equals" requires an additional column in the many-to-many table, and you ~could~ make it a boolean, but you can also get other advantages by making it an integer
i'm not sure guido was actually suggesting this, but at no time should you consider storing the "first among equals" attribute outside the database (for one thing, it would mean at least one row for every product)
guido2004 — 2010-09-22T11:51:54-04:00 — #5
Can't you avoid duplicate prod_id-status couples by putting another unique key on that pair of columns?
r937 — 2010-09-21T16:50:32-04:00 — #6
CREATE TABLE CategoriesXProducts
( cat_id INTEGER NOT NULL
, prod_id INTEGER NOT NULL
, PRIMARY KEY ( cat_id , prod_id )
, status INTEGER NOT NULL DEFAULT 100
you can link each product to the same category only once
r937 — 2010-09-21T15:12:52-04:00 — #7
use an integer instead of a boolean (especially a boolean that allows NULL!)
that way you can have "master status" of 100, 200, and so on
this would be a lot easier to hack for future requirements, e.g. major category plus minor category
guido2004 — 2010-09-21T15:02:42-04:00 — #8
I think you mean a many:many relationship?
the only structure I can think of that allows that is a column that is "is_master" where no is NULL and yes is 1.... but that's a nasty hack
A hack? No, it's a good solution. And nasty? Why?
brandonk — 2010-09-22T16:28:14-04:00 — #9
If I put an additional unique index on prod_id, status, then products would be limited to having two categories (status=200 for primary and status=100 for non-primary/whatever). The only way I know of to get around that is default status = null, but then I'm right back to my first post trying to avoid that.
guido2004 — 2010-09-23T15:16:59-04:00 — #10
I was saying that if the OP wants to have only 1 category (at the most) per product that has the value 1 (or whatever value he wants to use for his 'master category'), and he doesn't want to put a unique key on the product/status pair (because he doesn't want to use NULL as a value for all categories that don't have a status), then he'll have to enforce that restriction in his application code.
brandonk — 2010-09-23T15:10:46-04:00 — #11
Okay, I guess status_code it is. I'll just have to enforce the restriction of the "master status" in the application. Thanks for the tip about the status_code -- should be more useful than a simple boolean field.
r937 — 2010-09-22T17:58:40-04:00 — #12
brandonk — 2010-09-22T17:37:55-04:00 — #13
Yes, there is currently no limit to the number of categories a product can be in. Currently, we have products that are in more than 10 categories. This is why it is becoming harder for me to pick the master category given our current schema
r937 — 2010-09-22T17:30:53-04:00 — #14
can a product belong to more than one category? it looks like yes
can a product belong to more than two categories?
r937 — 2010-09-22T11:06:41-04:00 — #15
in my scheme, the master category would be the one with the lowest status number (actually, "priority" might be a better name than "status")
but there are many ways you can assign primacy ("first among equals"), just choose a design that gives you some flexibility (which a boolean does not)
brandonk — 2010-09-22T10:27:24-04:00 — #16
Right, that's how my current indexing works, but I'm curious if there was any way to enforce there only being 1 "master category" per product as well. Basically if I do a join to show a product's master category, I want to be able to know it will only return one row per product
brandonk — 2010-09-21T15:44:59-04:00 — #17
Sorry, yeah, it's a many to many. I always forget the first table has a bunch of records :duh:
It's a hack because it's an odd handling of NULL. NULL should not mean false -- the only reason I'd use it is because NULL values don't affect unique indices.
Rudy, using that method (a status_code), there's no way to enforce singularity/uniqueness/whatever the proper term is (outside of additional application logic) for each product. Any thoughts on that?