Using circular FK references in a database - mysql

I have two (mysql) tables -- company and user. They are structured as follows:
`user`
- id
- company_id (FK to company)
- name
`company`
- id
- name
- admin_user_id (FK to user)
The user table foreign keys to the company table, and vice versa.
I was wondering if the above pattern is ok, and if it's not why it's not, what could be done to improve it.

At a high level, your data model makes sense. However, you have no guarantee that admin_user_id points to a user in the same company. You can solve this by doing:
create table users (
user_id int auto_increment primary key,
company_id int,
name varchar(255),
unique (company_id, user_id) -- redundant but desirable for the foreign key reference
);
create table companies (
company_id int auto_increment primary key,
name varchar(255),
admin_user_id int,
foreign key (company_id, admin_user_id) references users(company_id, user_id)
);
alter table users
add constraint fk_users_company_id
foreign key (company_id) references companies (company_id);

If it is an accurate representation of your business domain then that's what really matters. There is a possible problem in SQL however. SQL only allows one table to be updated at once so either company or user has to come first. Normally you have to allow the constraint to be broken temporarily, either when you insert a new company with no corresponding user, or insert a new user with no corresponding company. To achieve that you can make one of the foreign keys nullable or you can temporarily disable the constraint.
Some data modellers dislike circular dependencies, even in purely conceptual models where the limitations of SQL are irrelevant. A few people will perceive circular dependencies as a modelling mistake - wrongly in my opinion. Disapproval of circular dependencies seems to be associated with ER modelling specifically. Interestingly, circular self-referential dependencies are taken for granted in Object Role Modelling which even has a special notation for them (ring constraints).

Related

Can I use 2 primary key (from same table) as foreign key in other table?

I am trying to develop a website. Is a kind of University website. I did a table named ACCOUNTS for admins,students and lecturer as well. So for admin I created manualy an account and this admin can manage things an website and he can create account for the lecturer and send them the details. For students i created a registrasion page where they can apply for a course, the admin decide how is accepted or not (i have a column in accounts named active and by default all account are created with inactive account). Now the problem is on GRADES table. the information which I need it is Grade-ID, Module-ID, theGrade and account_id for student to know how received the grade, but I would like to add the lecturer how marked as well, the problem is that it has same name (account_ID) because is in same table with the students. how should I do?
You can have two columns of the grades table that reference the primary key of the accounts table.
I think that you want something like:
create table grades(
grade_id int primary key,
grade int,
module_id int,
student_id int,
lecturer_id int,
constraint fk_grades_student
foreign key (student_id)
references accounts(account_id),
constraint fk_grades_lecturer
foreign key (lecturer_id)
references accounts(account_id)
);
Demo on DB Fiddle
Please note, however, that this does not ensure that the referenced account really is a lecturer or student (it just makes sure that it exists in accounts). It would be easier to enforce this referential constraint with a different data structure (for example, if there was separated child tables for each account role).

What to do when there's two Fk;s and either one of them is always null?

I have an application that has coaches, clients and workouts.
One coach can have many clients and one client can have many coaches = many to many
One coach can create many workouts and one workout can only be created by one coach = one to many
Also, one client can create many workouts and one workout can only be created by one client = one to many
My problem here is that a workout could only be created by either a coach or a client. Not both. What’s the best way to structure the tables and fields then?
Right now I have a workout that has id: Pk, coachId: Fk, clientId: Fk. But that means that either coachId or clientId are gonna be null on every row. I guess that’s not best practice?
I’ve also thought of just having one user table with a role table connected to it. But I don’t think that’s gonna be optimal/possible since a coach and a client are gonna have many different fields and relations (eg a coach could have certificates and diplomas and a client needs to be able to be assigned to a workout). Also a client need to be able to be assigned to one or many coaches and vice versa.
Current design:
Coach Client Workout CoachClient (composite)
id id id coachId Fk
coachId Fk clientId Fk
clientId Fk
You are on the right path by having two separate FKs (foreign keys) on the table workout.
Now, to make sure only one of the FKs is always null and the other FK is always NOT null you can add a constraint:
create table workout (
id int primary key not null,
coach_id int,
client_id int,
constraint fk1 foreign key (coach_id) references coach (id),
constraint fk2 foreign key (client_id) references client (id),
constraint chk1 check (coach_id is null and client_id is not null
or coach_id is not null and client_id is null)
);
The magic is in the last constraint chk1. It enforces that one and only one of them is not null.
Clients and Coaches are both Persons. So having one table solves your FK problem.
However, if there are a lot of differences in the columns of "clients" and "coaches", then this probably cause more problems than it cures.
Please note that
Foreign Keys have only a small number of properties; you are asking for more than they can give.
A Foreign key implicitly creates an index, to assist with performance; you can build the INDEX without having an FK.
A Foreign key is a 'constraint' that is checked at runtime. Since your complicated check can't be handled by an FK, maybe you should abandon the FK?

How do you model disjoint relationships between two relations in MySQL Workbench?

You have the option to uncheck Mandatory in the foreign key tab of the relationship window, but that doesn't fully capture the meaning of a disjoint relationships, which is an EITHER-OR relationship between multiple relations.
Your referring to the mandatory property of the foreign key makes me believe you are either misunderstanding the meaning of a disjoint relationship, or implementing it with a relation in the wrong "direction".
Let's say we want to implement the following schema:
class: Staff Member
class: Permanent (specialises Staff Member)
class: Temporary (specialises Staff Member)
a Staff Member is either a Permanent employee or a Temporary contractor
A corresponding EER schema would be (MySQL syntax):
CREATE TABLE staff_member (
id INT PRIMARY KEY,
name VARCHAR(20) NOT NULL
);
CREATE TABLE permanent (
id INT PRIMARY KEY,
next_appraisal DATETIME NOT NULL,
FOREIGN KEY (id) REFERENCES staff_member(id)
);
CREATE TABLE temporary (
id INT PRIMARY KEY,
contract_end DATETIME NOT NULL,
FOREIGN KEY (id) REFERENCES staff_member(id)
);
Notice the foreign key is from the specialised entity to the parent entity (id being the primary key, it is also always mandatory by definition).
This still doesn't answer your question. How to model the disjoint property of this relationship? You cannot do this easily (neither can you model that a specialisation is complete, by the way).
Many RDBMS support the use of CHECK constraints in order to enforce these extra conditions, but MySQL does not (beware, the syntax is accepted by the MySQL parser, but the declaration is ignored). However, simple workarounds exist that result in the same effect.

Class Table Inheritance (CTI) and Insert Select statements

I am working to build an inheritance database model within MySQL, such that all tables inherit from one base type (object), represented by the object table. This allows for Notes to be linked to any object from any table within the database while retaining referential integrity. The design looks something like this (there are a lot more child tables with similar structures):
CREATE TABLE object
(
object_id INT(10) AUTO_INCREMENT,
object_type VARCHAR(80),
PRIMARY KEY (object_id)
);
CREATE TABLE person
(
person_id INT(10),
name_first VARCHAR(80),
name_last VARCHAR(80),
email_address VARCHAR(80),
PRIMARY KEY (person_id),
CONSTRAINT fk_person FOREIGN KEY (person_id)
REFERENCES object (object_id)
);
CREATE TABLE note
(
note_id INT(10),
not_text TEXT,
note_subject_id INT(10),
PRIMARY KEY (note_id),
CONSTRAINT fk_note FOREIGN KEY (note_id)
REFERENCES object (object_id),
CONSTRAINT fk_note_subject FOREIGN KEY (note_subject_id)
REFERENCES object (object_id)
);
With this design, I am able to make a note with a person as the subject, a note with another note as the subject, or a note with one of the many other tables inheriting from object as a subject (these tables are not listed for brevity). Although it cannot be enforced through referential integrity, a presupposition of this design is that each object_id is used in only one row of one child table, so that there are no notes where the note_id is also a person_id.
The problem occurs when I want to perform INSERT... SELECT statement on person or note. Let's say that I have a user table and I would like to insert all users in to person. First I have to insert the number of new person rows I am creating into object, then I need to insert the new rows into person, but I have no way of matching each user row to an object row in order to populate the person_id column.
My first thought was to create a BEFORE INSERT TRIGGER on person that would create the new object record and update the NEW.person_id value accordingly. Unfortunately, the foreign key constraint is being evaluated before the trigger is allowed to fire, catching my orphaned row before I can correct it.
What I am looking for is either a way to change the order of constraint execution so that BEFORE INSERT triggers precede constraints, or for a more elegant way to achieve an Class Table Inheritance database structure within MySQL.
This is not object orientation, you just want to implement inheritance in your RDBMS. You have three choices: Horizontal Mapping, Vertical Mapping, Filtered Mapping.
Reference: http://modeling.sourceforge.net/UserGuide/design-inheritance.html
You may not need inheritance though, if you relax a little on reference integrity. Your Note table may contain multiple nullable foreign keys, one for each table you want to add the note to.

MySQL - autoincrement + compound primary key - performance & integrity

I have a database design that makes use of compound primary keys to ensure uniqueness and which are also foreign keys.
These tables are then linked to other tables in the same way, so that in the end the compound key can get up to 4 or 5 columns. This led to some rather large JOINs, so I thought a simple solution would be to use an autoincrement column which is not part of the primary key but which is used as part of the primary key of other table(s).
Here is some pseudo code showing the general layout :
CREATE TABLE Item (
id AUTO_INCREMENT,
...
PRIMARY KEY (id)
) ENGINE = InnoDB;
CREATE TABLE PriceCategory (
id AUTO_INCREMENT,
...
PRIMARY KEY (id)
)
CREATE TABLE ItemPriceCategory (
itemId,
priceCategoryId,
id AUTO_INCREMENT,
...
UNIQUE INDEX id,
PRIMARY KEY (eventId, priceCategoryId)
)
CREATE TABLE ClientType (
id AUTO_INCREMENT,
...
PRIMARY KEY (id)
)
CREATE TABLE Price (
itemPriceCategoryId,
clientTypeId,
id AUTO_INCREMENT,
...
UNIQUE INDEX id,
PRIMARY KEY (itemPriceCategoryId, clientTypeId)
)
table Purchase (
priceId,
userId,
amount,
PRIMARY KEY (priceId, userId)
)
The names of tables have been changed to protect the innocent ;-) Also the actual layout is a little deeper in terms of references.
So, my question is, is this a viable strategy, from a performance and data integrity point of view ? Is it better to have all keys from all the referenced tables in the Purchase table ?
Thanks in advance.
Generally, the advice on primary keys is to have "meaningless", immutable primary keys with a single column. Auto incrementing integers are nice.
So, I would reverse your design - your join tables should also have meaningless primary keys. For instance:
CREATE TABLE ItemPriceCategory (
itemId,
priceCategoryId,
id AUTO_INCREMENT,
...
PRIMARY KEY id,
UNIQUE INDEX (eventId, priceCategoryId)
)
That way, the itemPriceCategoryId column in price is a proper foreign key, linking to the primary key of the ItemPriceCategory table.
You can then use http://dev.mysql.com/doc/refman/5.5/en/innodb-foreign-key-constraints.html foreign keys to ensure the consistency of your database.
In terms of performance, broadly speaking, this strategy should be faster than querying compound keys in a join, but with a well-indexed database, you may not actually notice the difference...
I think that something has been lost in translation over here, but I did my best to make an ER diagram of this.
In general, there are two approaches. The first one is to propagate keys and the second one is to have an auto-increment integer as a PK for each table.
The second approach is often driven by ORM tools which use a DB as object-persistence storage, while the first one (using key propagation) is more common for hand-crafted DB design.
In general, the model with key propagation offers better performance for "random queries", mostly because you can "skip tables" in joins. For example, in the model with key propagation you can join the Purchase table directly to the Item table to report purchases by ItemName. In the other model you would have to join Price and ItemPriceCategory tables too -- just to get to the ItemID.
Basically, the model with key propagation is essentially relational -- while the other one is object-driven. ORM tools either prefer or enforce the model with separate ID (second case), but offer other advantages for development.
Your example seems to be trying to use some kind of a combination of these two -- not necessarily bad, it would help if you could talk to original designer.
With key propagation
Independent keys for each table