Question

I will explain my case with an example.

I have the following tables: persons, places, pictures. Both persons and places have many pictures. What is the best way to express this in a database scheme while maintaining referential integrity?

  1. I could add a column for each possible association, but this will create a lot of empty fields when there are other things that have pictures, too.
  2. I could create association tables between persons and pictures and places and pictures. But then the foreign keys would be in the association table and I cannot enforce that pictuers are deleted when places etc. are deleted.

At the moment I lean towards the second approach, but I do not like it.

Was it helpful?

Solution

There's no one-size fits all solution that will tick all of the boxes you want. But one I've seen before is

3) Introduce a base table for items that will share relationships:

CREATE TABLE Entities (
    EntityID int not null primary key,
    EntityType varchar(10) not null,
    constraint CK_EntityTypes CHECK (EntityType in ('Person','Place')),
    constraint UQ_Entities_WithTypes UNIQUE (EntityID,EntityType)
)

And then build your People and Places tables:

CREATE TABLE People (
    PersonID int not null PRIMARY KEY,
    EntityType AS CAST('Person' as varchar(10)) persisted,
    ...Other columns...
    constraint FK_People_Entities FOREIGN KEY (PersonID,EntityType) references Entities (EntityID,EntityType)
)

CREATE TABLE Places (
    PlaceID int not null PRIMARY KEY,
    EntityType AS CAST('Place' as varchar(10)) persisted,
    ...Other columns...
    constraint FK_Places_Entities FOREIGN KEY (PlaceID,EntityType) references Entities (EntityID,EntityType)
)

(I'm not sure Entities is exactly right when thinking about places - a better name may suggest itself to you).

You can then have Pictures just references the EntityID.


Otherwise, if I had to pick between 1 & 2, I'd usually recommend 1. Unless the number of "types" involved gets large, it still doesn't make the Pictures table too wide, and as you've observed, you can at least use the normal FK mechanisms to enforce cascades, if necessary.


4) If Pictures is just a very bare table at the moment, maybe query whether there ought to be one pictures table, or one per type. Will pictures of people and pictures of places frequently end up queried together (and even if they are, could a UNION ALL based query hide the fact that you're using separate tables)?

OTHER TIPS

I think there are three ways. One is to have separate tables mapping each entity to the pictures. Something like:

create table PersonPictures . . .
    PersonId int not null,
    PictureId int not null

. . .

create table PlacesPictures . . .
    PlaceId int not null,
    PictureId int not null

This works when Persons and Places are separate entities and might have other information about the picture, that differs from each other (say what the person was wearing or the viewpoint of place).

If Persons and Places are really just attributes of a picture, then you can have:

create table AttributesPictures . . .
    PictureId int not null,
    AttributeType varchar(255),  -- 'Person', 'Place'
    PersonId int,
    PlaceId int

In this case, you can set up constraints to ensure that AttributeType only takes on well-defined values (or use and AttributeId with a separate lookup table). And constraints on the foreign keys: when AttributeType = 'Person' then PersonId is not null and Place Id is null.

If a picture has at most exactly one person and exactly one place, then you can store these ids in the picture record itself -- a great convenience in this special case.

Which approach you take should be driven more by the application requirements. In any approach, you can use foreign key relationships and cascading deletes to ensure that records get deleted as appropriate. Alternatively, you can also use triggers to enforce relational integrity (although that is not my favorite solution).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top