4

I have three tables in a MySQL DB. This is the main table with organisation related stuff. Every Organisation has an unique identifier which is also the foreign key in some tables.

org
+------------+-------------+ 
| org_id     | name        |   
+------------+-------------+ 
| 1          | a           | 
| 2          | b           |  
| 3          | c           |  
+------------+-------------+

This is the groups table. Organisations can have many groups.

groups
FOREIGN KEY (ORG_ID) REFERENCES ORG (ID);
+------------+-------------+----------+ 
| ID         | org_id      |   name   |
+------------+-------------+ ---------+
| 1          | 1           |  Group1  |
| 2          | 2           |  Group2  |
| 3          | 2           |  Group3  |
+------------+-------------+----------+

And this is the feed table in which I would like to perform an update. A feed can have only one associated group.

feed
FOREIGN KEY (GROUP_ID) REFERENCES GROUPS (ID);
+------------+-------------+--------------+ 
| ID         | org_id      |   group_id   |
+------------+-------------+ -------------+
| 1          | 1           |      1       |
| 2          | 2           |      2       |
| 3          | 1           |     NULL     |
| 4          | 2           |      3       |
+------------+-------------+--------------+

So now there is one problem, that i can't solve. When I INSERT or UPDATE a row, I set the groups_id, but this can also be a groups_id which not belongs to the organisation. This happens, because all ID's in GROUPS are valid FK values. That's a thing I want to avoid. It should only be possible to insert or update a row with a groups_id which has also the same org_id as in feeds.org_id.

As you can see, the data is now fine. But when I try to make this INSERT INTO feed VALUES (4, 2, 1) it were nice to see an error. Yeah, right, I'm missing an lovely error....

It is difficult for me to make an connection between them. There seems one information or method that I'm missing. I've been looking for a lot, but I don't know the words to describe my problem.

So I ask you, could you give me a tip?

EDIT:

All feeds and all groups are related to an organisation, which has an identifier. An organisation can create feeds/messages. When this feeds are not associated with a group, this feed ist public. For special feeds they can create a group. This group is related to this special organisation.

This works and everything is good:

UPDATE feed 
SET title = "Title", message = "Message", groups_id = "1" 
WHERE id = "1" AND org_id = "1"

But this works also:

UPDATE feed 
SET title = "Title", message = "Message", groups_id = "2" 
WHERE id = "1" AND org_id = "1"

The problem is, that it is possible to associate a group to a feed (which is associated to org 1), while the group is not associated with the org (group 2 is associated with org 2).

So my thought was, is there a way to solve this through FOREIGN KEY or similar (checks, joins, subqueries). Or should I think about my db design?

Keev
  • 98
  • 6
  • Your mistake is including `org_id` in `feed` in the first place. It should be derived through the `groups` table by the `group_id`. – shmosel Dec 19 '16 at 22:30
  • 1
    why does org able need an id and org_id both? – e4c5 Dec 19 '16 at 22:45
  • @shmosel The thing is, I need the org_id in FEED to associate the feeds, where is no groups_id given. All rows who are not linked to an group, are NULL and automatically in the public group. But only to the given org_id. – Keev Dec 19 '16 at 22:50
  • You're saying not all feeds have a group, but they all have an org? Interesting. Would it be feasible to define a separate public group for each org? – shmosel Dec 19 '16 at 22:52
  • @e4c5 this is a simpler illustration. org table is normally the user table and a organisation can have many user. thought it would be so easyer – Keev Dec 19 '16 at 22:52
  • 1
    I think you need to post an MCVE http://stackoverflow.com/help/how-to-ask – e4c5 Dec 19 '16 at 22:53
  • @e4c5 you're right. I will try to provide a better explanation. – Keev Dec 19 '16 at 22:58
  • Please could you specify clearly the relationships that you intend to model? So, you have three entity types; org, group, and feed. From what I can see, each Group can only belong to one Org, but an Org can have many Groups or even no Groups. The way I represent that is `Orgs(1) : (0..many)Groups` *(An Org can have 0 to many associated Groups, a Group can and must have 1 associated Org)* Then, I believe you also have `Groups(1) : (0..many)Feeds`? In which case your `feeds` table doesn't need the `org_id` field, just the `group_id` field – MatBailie Dec 19 '16 at 23:25

1 Answers1

3

I think a composed foreign key solves your problem:

create table agroup (
  id int primary key,
  orgid int,
  UNIQUE (id,orgid)
);

create table feed (
  id int primary key,
  groupid int,
  orgid int,
  FOREIGN KEY (groupid, orgid) REFERENCES agroup(id, orgid)
);

insert into agroup values (10, 1), (20, 1), (30, 2), (40, NULL);
insert into feed values (100,10,1), (101, 20, 1);
insert into feed values (102, 40, NULL); # works
insert into feed values (103, NULL, 1); # works as well
# insert into feed values (110,10,2); # yields error "Cannot add or update a child row: a foreign key constraint fails"

Note the UNIQUE(id,orgid), which seems to be necessary. Though I do not understand why agroup(id primary key) is not sufficient to make also agroup(id,orgid) unique, I got a compiler error without this explicit unique(id,orgid)-constraint. Documentation says that the referenced attributes must be indexed. Anyway, your problem should be solved.

EDIT: Extended example, which now demonstrates also the case of NULL-values in referencing attributes.

At least in MySQL, a composite foreign key constraint permits NULL values in the referencing (child) rows, regardless of whether the parent table contain rows with corresponding NULL-values or not. If one inserts a row with NULL-values for foreign-key attributes, the foreign key constraint is simply ignored. Confer mysql foreign key semantics, which says: "... MySQL essentially implements the semantics defined by MATCH SIMPLE, which permit a foreign key to be all or partially NULL. In that case, the (child table) row containing such a foreign key is permitted to be inserted, and does not match any row in the referenced (parent) table. It is possible to implement other semantics using triggers."

Stephan Lechner
  • 34,891
  • 4
  • 35
  • 58
  • damn it didn't occur to me that the foreign key can be composed like this because the org_id obviously references the other table, yes so this is right! – e4c5 Dec 19 '16 at 23:28
  • There is the corner case of 'public' feeds which have no group *(group_id)* IS NULL. But that would break your foreign key constraint. Instead, there should be an explicit group for each organisation called 'public', so as to no longer need NULL values – MatBailie Dec 19 '16 at 23:31
  • I will test this tomorrow intensely, but it seems to solve my problem. thank you a lot! @MatBailie I thought, I could save up an entry. I will rework my shema. thx. – Keev Dec 20 '16 at 00:10
  • FYI: You also can create a composite [**primary key**](http://stackoverflow.com/questions/5835978/how-to-properly-create-composite-primary-keys-mysql) – Juan Carlos Oropeza Dec 20 '16 at 13:54
  • I've tested it, it works perfect! This is exactly the behavior I wanted. That I can use NULL values furthermore is awesome. So I dont have to setup an extra group, that isn't actually needed. Thanks a lot! – Keev Dec 20 '16 at 19:10
  • if you want to avoid NULL values, simply make the respective columns "NOT NULL", e.g. feed(..., groupid int not null, ...) – Stephan Lechner Dec 20 '16 at 20:55