By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
437,933 Members | 1,676 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 437,933 IT Pros & Developers. It's quick & easy.

Tables Referencing themselves As Foreign Keys

P: n/a
Hi,

I'm still new to this so if I'm sounding dumb or my premise is flawed
please forgive me. I have a DB design which contains a table which has
categories, each category has a parent category, and is recursed until
the top category is reached, in order to create breadcrumbs. Is there
any problem with using foreign keys to reference the same table? So a
when category is added the CatParent MUST be present as a CatID

CatID - Serial
CatParent - int4 - References CatID
CatName - Text

Am I likeley to come unstuck with this?

Cheers

T.



---------------------------(end of broadcast)---------------------------
TIP 8: explain analyze is your friend

Nov 12 '05 #1
Share this Question
Share on Google+
6 Replies


P: n/a
Tony,

That'll work, but you have to mind the first row/toprow you insert.
Will it have no parent (make the field nullable) or will it be its own
parent (you'll have to test whether that works, I don't know, foreign
keys are deferrable, so it should be possible if you specify that).

Best regards,

Arjen

Tony (Unihost) wrote:
Hi,

I'm still new to this so if I'm sounding dumb or my premise is flawed
please forgive me. I have a DB design which contains a table which has
categories, each category has a parent category, and is recursed until
the top category is reached, in order to create breadcrumbs. Is there
any problem with using foreign keys to reference the same table? So a
when category is added the CatParent MUST be present as a CatID

CatID - Serial
CatParent - int4 - References CatID
CatName - Text

Am I likeley to come unstuck with this?

Cheers

T.


---------------------------(end of broadcast)---------------------------
TIP 8: explain analyze is your friend

Nov 12 '05 #2

P: n/a
Arjen van der Meijden wrote:
Tony,

That'll work, but you have to mind the first row/toprow you insert.
Will it have no parent (make the field nullable) or will it be its own
parent (you'll have to test whether that works, I don't know, foreign
keys are deferrable, so it should be possible if you specify that).


A more traditional way to have hierarchical relationships in the
relational model is to have two relations (and not use NULLs):

CREATE TABLE categories (
CatID bigint PRIMARY KEY NOT NULL,
CatName text NOT NULL
);

CREATE TABLE category_parents (
CatID bigint UNIQUE NOT NULL REFERENCES categories(CatID),
ParentID bigint NOT NULL REFERENCES categories(CatID)
CHECK (CatID <> ParentID)
);

The top category would be the only tuple in categories that did not
exist in category_parents.

HTH,

Mike Mascari
ma*****@mascari.com

---------------------------(end of broadcast)---------------------------
TIP 2: you can get off all lists at once with the unregister command
(send "unregister YourEmailAddressHere" to ma*******@postgresql.org)

Nov 12 '05 #3

P: n/a
Mike Mascari wrote:
Arjen van der Meijden wrote:
Tony,

That'll work, but you have to mind the first row/toprow you insert.
Will it have no parent (make the field nullable) or will it be its own
parent (you'll have to test whether that works, I don't know, foreign
keys are deferrable, so it should be possible if you specify that).

A more traditional way to have hierarchical relationships in the
relational model is to have two relations (and not use NULLs):

CREATE TABLE categories (
CatID bigint PRIMARY KEY NOT NULL,
CatName text NOT NULL
);

CREATE TABLE category_parents (
CatID bigint UNIQUE NOT NULL REFERENCES categories(CatID),
ParentID bigint NOT NULL REFERENCES categories(CatID)
CHECK (CatID <> ParentID)
);

The top category would be the only tuple in categories that did not
exist in category_parents.


What you're modelling here is a general graph, not a tree.
This model allows to have multiple parents for children, parents to be
their childrens child, etc.

The singletable model is just a tree, nothing more. If you want the
above model to resemble a tree, you'd make sure that a tuple cannot be
the child of any of its children and a child can have only one parent.
And that would force you to create triggers, while the other model just
enforces that due to its structure :)
If you *need* a graph, then yes, that's the most traditional way.

Best regards,

Arjen

---------------------------(end of broadcast)---------------------------
TIP 1: subscribe and unsubscribe commands go to ma*******@postgresql.org

Nov 12 '05 #4

P: n/a
Arjen van der Meijden wrote:
Mike Mascari wrote:
A more traditional way to have hierarchical relationships in the
relational model is to have two relations (and not use NULLs):

CREATE TABLE categories (
CatID bigint PRIMARY KEY NOT NULL,
CatName text NOT NULL
);

CREATE TABLE category_parents (
CatID bigint UNIQUE NOT NULL REFERENCES categories(CatID),
ParentID bigint NOT NULL REFERENCES categories(CatID)
CHECK (CatID <> ParentID)
);

The top category would be the only tuple in categories that did not
exist in category_parents.

What you're modelling here is a general graph, not a tree.
This model allows to have multiple parents for children


The UNIQUE constraint prevents that.
parents to be their childrens child, etc.
Nothing in the single-relation design prevents that, either:

INSERT INTO categories (CatID, ParentID) VALUES (1, 1); <- Root Node
INSERT INTO categories (CatID, ParentID) VALUES (2, 1);
INSERT INTO categories (CatID, ParentID) VALUES (3, 2);

UPDATE categories SET ParentID = 3 WHERE CatID = 2;

A CHECK constraints composed of a recursive function (transitive closure
check) will be necessary in *both* designs. If you are just growing a
tree and children never change parents, in both designs, one might be
able to achieve the goal with a check constraint:

CHECK (CatID > ParentID)
The singletable model is just a tree, nothing more. If you want the
above model to resemble a tree, you'd make sure that a tuple cannot be
the child of any of its children and a child can have only one parent.
And that would force you to create triggers, while the other model
just enforces that due to its structure :)
Not quite. See above. In addition, there's nothing, without the use of a
partial + functional index, or some other hackery, to prevent:

INSERT INTO categories (CatID, ParentID) VALUES (1, 1);
INSERT INTO categories (CatID, ParentID) VALUES (2, 2);

which may or may not be what you want. The same holds true of the
two-relation variable design as well, of course.
If you *need* a graph, then yes, that's the most traditional way.


I'm in the Christmas spirit, so I will add some weight to your argument.
:-)

If the purpose of the two-relation variable design is to eliminate the
use of NULLs and/or the self-referencing parent, I fail to see how one
could have an ON DELETE CASCADE RI constraint to delete the descendants
of a parent which has been deleted, unless the constraint is added after
the first generation of children have been created. A minor detail the
anti-NULL adherents, and I usually include myself in that category,
haven't expounded upon...

Mike Mascari
ma*****@mascari.com

---------------------------(end of broadcast)---------------------------
TIP 6: Have you searched our list archives?

http://archives.postgresql.org

Nov 12 '05 #5

P: n/a
On Mon, 2003-12-22 at 05:57, Tony (Unihost) wrote:
Hi,

I'm still new to this so if I'm sounding dumb or my premise is flawed
please forgive me. I have a DB design which contains a table which has
categories, each category has a parent category, and is recursed until
the top category is reached, in order to create breadcrumbs. Is there
any problem with using foreign keys to reference the same table? So a
when category is added the CatParent MUST be present as a CatID


Hmm... breadcrubs make me think you should look at using a nested set.
(Google "nested set celko" for more info)

Robert Treat
--
Build A Brighter Lamp :: Linux Apache {middleware} PostgreSQL
---------------------------(end of broadcast)---------------------------
TIP 4: Don't 'kill -9' the postmaster

Nov 12 '05 #6

P: n/a
This is a fine approach. The FK will work fine. You'll probably want CatID
to be NOT NULL and CatParent to allow nulls. Having a Null parent
indicating root is easier for traversals.

Common other features to add include:
a "path" column that is maintaned by insert/update triggers. Quite
easy to do and very helpful.
Once you have that you can do a simple test for circularity also on
insert/update, like:
IF "path" ~ '(^|\\.)' || "CatID"::text || '(\\.|$)' THEN
RAISE EXCEPTION ''circular hierarchy detected...'';
END IF;
There's also a short-cut way to do this since you use Serial for the CatIDs.
Just do a CHECK (CatParent < CatID) -- of course it makes an assumption
about the CatIDs really come in serially...

== Ezra Epstein
""Tony (Unihost)"" <to**@unihost.net> wrote in message
news:3F**************@unihost.net...
Hi,

I'm still new to this so if I'm sounding dumb or my premise is flawed
please forgive me. I have a DB design which contains a table which has
categories, each category has a parent category, and is recursed until
the top category is reached, in order to create breadcrumbs. Is there
any problem with using foreign keys to reference the same table? So a
when category is added the CatParent MUST be present as a CatID

CatID - Serial
CatParent - int4 - References CatID
CatName - Text

Am I likeley to come unstuck with this?

Cheers

T.



---------------------------(end of broadcast)---------------------------
TIP 8: explain analyze is your friend

Nov 12 '05 #7

This discussion thread is closed

Replies have been disabled for this discussion.