Many table logs relation - relational-database

I have a problom about my ERD...
I have 3 user table with different attributes. Each user connected to house table.
Then house table has many logs. My question is, how to know the logs are related to users depends on user type

Add a USER table bridging the USER_x tables to HOUSE:
-- [id] is a type x user and ...
USER_x(id,...) PK (id) FK (id) REFERENCES USER (id)
-- [id] is a user
USER(id) PK (id)
-- house [id] is used by [USER_id] and ...
HOUSE(id,USER_id,...) PK (id) FK (USER_id) REFERENCES USER (id)
There are various ways to constrain this kind of design so that USER has exactly the disjoint union of USER_x ids. Eg a USER type discriminator column whose values are the various values for x. (Also other kinds of design. Eg a nullable USER foreign key per USER_x. With their own constraints. Eg checking that only one such FK IS NOT NULL.)
Search for Stackoverflow SQL articles re subtables, subtypes, inheritance and foreign keys. Eg this or this.
Here's how to constraint this kind of situation without triggers.

Related

Using circular FK references in a database

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).

Featured value in one to many relation, which table should hold that?

say that i have a one to many relations where there are two tables, a Person table and a Belonging table. Now, each Person has ONLY ONE favorite belonging and a specific belonging cannot belong to another person as well.
My question is, where would that information be better kept ? In the Person table as a favorite_belonging_id or in the Belonging table as an is_favorite entry ? To my eyes, the first choice seems to be the better version, but I would like to hear what sql knowledgeable people have to say about it.
EDIT : A Person has many belongings but only ONE favorite belonging and each belonging can only belong to one person. It's a one to many association.
I'd be tempted to go with your first suggestion (a favourite_belonging_id column in the Person table), as one can then create a foreign key reference from (person_id, favourite_belonging_id) to (owner_id, belonging_id) in the Belonging table.
If one were to go the other route of creating a is_favourite flag in the Belonging table, there is no obvious way of ensuring the 1:1 nature of person-favourite belonging relationships (a composite UNIQUE index over (owner_id, is_favourite) would fail when a person has multiple belongings that are not their favourite).
That said, it doesn't feel like this information really belongs in the Person table, as it isn't really a property of the person but rather it's a property of the Belonging. If you feel strongly about it, you could create a Favourites table that has a UNIQUE (or PRIMARY) index over person_id.
to me it does NOT belong in the person table since it has nothing to do with the base person.
if you have only the belonging table - which i also assume has a person_id in it, then this is where you are expressing the relationship between the belonging and the person, and it is where the qualifier should also go.
another option is to have a third table in the middle linking the two - in this case, the favorite flag goes there.
edit:
my preference in design would be the third table option - here you can put a begin date and end date as well as the favorite flag - this would allow you to theoretically trade a belonging to another person at some point in time and still know what happened.
I see that pretty much all the different options have already been laid out in different answers, but instead of commenting on all to give you my impression on what I think you should do, I'll just create an answer myself.
Just to be clear on how I understand how the system works: All users can have multiple belongings, but any belonging can only be help by one person.
In this case, it makes the most sense to have a user_id in the belongings table that can tie a belonging to a person. Once a user_id is set, nobody else can claim it anymore.
Now, as to the 'favorite' part, there are several things you can do. What truly is the best way to do it strongly depends on the queries you plan on running on it. Some consider adding a JOIN table, but honestly this is a lot of additional data that is rather pointless; there is likely going to be the exact amount of rows in it as the user table and by putting it in a separate table, there is a lot you can't do (for example, see how many people DON'T have a favorite). Likewise, a JOIN table would make no sense for the user_belonging relationship, as there is a 1:1 relationship between the belonging and the amount of people who can have it.
So I believe there are two viable options: either add a field (/switch) in the belongings table to indicate of a user's belonging is his/ her favorite, or add a field to the user table to indicate which belonging is the user's favorite. I would personally think that the latter holds the most merit, but depending on the queries you run, it might make more sense to to the former. Overall, the biggest difference is whether you want to process things pre-insert or post-select; e.g. in the latter situation, you will have to run an independent query to figure out if the user already has a favorite (in the former case this won't be necessary as you would put a unique index on the field in the user table), whereas in a post-select situation you will have to do cross reference which of the selected belongings from the belonging table is the user's favorite.
Please let me know if I explained myself clearly, or if you have any further questions.
The following may not be the best options because it offers a somewhat unconventional method of flagging the favourite belonging. The advantage, though, is that this way you'll have just two tables with no circular references and every person will be guaranteed to have no more than one favourite belonging.
So, it's two tables, people (or persons) and belongings. The people table has this structure:
person_id INT AUTO_INCREMENT,
other columns as necessary,
PRIMARY KEY (person_id)
The belongings table is created like this:
belonging_id INT AUTO_INCREMENT,
person_id INT NOT NULL,
is_favourite enum ('1'),
other columns as necessary,
PRIMARY KEY (belonging_id),
FOREIGN KEY (person_id) REFERENCING people (person_id),
UNIQUE (person_id, is_favourite)
The key element is declaring is_favourite as a nullable enum with a single possible value. This way, when you declare a unique constraint on the pair of (person_id, is_favourite), you are allowed to have as many rows with the same person_id and empty (null) is_favourite as possible, because unique constraints ignore rows where at least one member is null. And you won't be able to create more than one person_id with is_favourite = '1', because that would violate the unique constraint.
Neither. My suggestion is to add another table person_favourite_belonging, like this:
CREATE TABLE person
( person_id INTEGER NOT NULL
--- various other columns about Persons
, PRIMARY KEY (person_id)
) ;
CREATE TABLE belonging
( belonging_id INTEGER NOT NULL
, person_id INTEGER NOT NULL
--- various other columns about Belongings
, PRIMARY KEY (belonging_id)
, UNIQUE KEY (person_id, belonging_id) --- this Unique constraint is needed
, FOREIGN KEY (person_id)
REFERENCES person (person_id)
) ;
CREATE TABLE person_favourite_belonging
( person_id INTEGER NOT NULL
, belonging_id INTEGER NOT NULL
, PRIMARY KEY (person_id)
, FOREIGN KEY (person_id, belonging_id) --- for this Foreign Key constraint
REFERENCES belonging (person_id, belonging_id)
) ;
This is just my preferred way of doing this. There are alternatives and all have their pros and cons. The pros with this approach are:
No circular path in the Foreign Key constraints (and therefore):
No chicken and egg problems when inserting, deleting or updating Persons, Belongings or Favourite Belongings.
All foreign key columns can be defined as NOT NULL.
The integrity can be enforced at the database level.
If your requirements change and you want to have 2 (or more) favourites per person, you only change appropriately the constraints at the Favourite table.
Check also my answer in this question (with an almost identical problem): In SQL, is it OK for two tables to refer to each other?
favourite_thing is a FK to the belonging table (if that table exists, otherwise it could be a domain) , but in an additional constraint, you can force belonging_id in the persons table to be unique.
UPDATE:
DROP table belonging;
CREATE table belonging
( id INTEGER PRIMARY KEY
, description varchar
);
DROP table person;
CREATE table person
( id INTEGER PRIMARY KEY
, description varchar
, favourite_thing INTEGER REFERENCES belonging (id)
);
-- Now add the unique constraint
-- NOTE: favourite_thing can still be NULL
ALTER TABLE person
ADD CONSTRAINT must_be_unique UNIQUE (favourite_thing)
;
UPDATE 2: if every belonging belongs to exactly one person, you could add an owner field to belongings:
CREATE table belonging
( id INTEGER PRIMARY KEY
, owner_id INTEGER NOT NULL REFERENCES person(id)
, description varchar
);
DROP table person CASCADE;
CREATE table person
( id INTEGER PRIMARY KEY
, description varchar
, favourite_thing INTEGER REFERENCES belonging (id)
);
ALTER TABLE person
ADD CONSTRAINT must_be_unique UNIQUE (favourite_thing)
;
Actually you present a one-to-one relation.
So you can:
1. Hold it in Person table.
2. Hold it in Belonging table.
3. Hold it in both.
4. Hold it in separate table.

Can someone help me understand the foreign key UPDATE and DELETE vs relationship name?

Currently, I am working on the very beginnings of a user database in MySQL InnoDB. So, I will use that to help demonstrate.
Users Roles
--------------- ---------------
userid (bigint) PK >roleid (tinyint) PK
email (varchar) rolename (varchar)
username (varchar)
password (char)
>roleid (tinyint) FK
created (timestamp)
Right now, I am trying to create a one-to-many relationship between Role and Users, based on roleid. For this, I am thinking that On UPDATE CASCADE and On DELETE Restrict.
That's what I feel like I should do, I'm not sure if it's correct or not. However, I'd like to gain a better understanding of it.
Say I wanted to created one-to-one, then that would look like On UPDATE Restrict and On DELETE Restrict, is that correct?
Sorry, I am completely confused here and I am unable to find a tutorial, blog, or explanation that breaks down the different settings to the Relational Model. Could anyone help explain it these types as well as the other types (many-to-many, many-to-one) based on what I have here?
One role can have many users associated to that role. This entire relationship is represented physically with a foreign key on the USERS table that references the ROLES table.
The ON UPDATE and ON DELETE options in the foreign key constraint help to enforce "referential integrity" in the database, but they don't specify at all what the relationship is between the USERS and ROLES entities.
If I create this foreign key with ON DELETE RESTRICT, when I would try to delete a record from the ROLES table where the key was in use on the USERS table, i would get an error. This has nothing to do with the type of logical relationship that exists - it is just a constraint.
A many to many relationship cannot be modeled using one foreign key. Logically, if A user can have Many Roles, storing the role id on the users table doesn't make sense.
In that case, you would create a table in between, and put the userid and roleid columns on this table, with foreign keys connecting them to users and roles respectively.
USERS USERS_ROLES ROLES
userid PK - userid FK
roleid FK - roleid PK
Here's the MYSQL manual pages about foreign key constraints. It's a good reference and explains what each option means.
Edit:
This touched on the one-to-many and many-to-many relationship types. Rarely will you see a one-to-one type in a database (in those cases merging the tables makes sense). Sometimes for performance you would use it. In these cases, typically your primary key should be the same on both tables:
USERS USERS_EXTENDED_ATTRIBUTES
userid PK - userid PK FK
Only 1 user id should exist on each table for a 1-1 relationship.

One to Many MySQL [duplicate]

This question already has an answer here:
Closed 11 years ago.
Possible Duplicate:
MySQL Relationships
I am trying to create a one to many relationship in MySQL with foreign keys.
Two tables, user and location. Each user can have many locations, but each location can have only one user.
How do I configure this? I am using HeidiSQL if that helps, though I can input code as well.
MySQL does not know, nor does it need to know if a relationship is 1-1, or 1-many.
No SQL supports many-many relationships, all require a intermediate table which splits a many-many relationship into 2 separate 1-many.
The difference is in the logic that controls the relationships, which is in the code that you write.
A 1-1 relationship is maintained by having the tables share the same primary key (PK).
With the secondary table declaring that PK as a foreign key pointing to the other tables PK.
Table chinese_mother (
id integer primary key,
name....
Table chinese_child (
id integer primary key,
name ....
....,
foreign key (id) references chinese_mother.id
The direction of the relationship 1 -> many vs many <- 1 is determined by the location of the link field.
Usually every table has a unique id and the link field is called tablename_id.
The table that has the link field in it is the many side of the relationship, the other table is on the 1 side.
Each user can have many locations, but each location can have only one user.
Table user
id: primary key
name......
.....
Table location
id: primary key
user_id foreign key references (user.id)
x
y
.......
By placing the link field in the location table, you force things so that a location can only have 1 user. However a user can have many locations.
There is an example here that is almost exactly what you need foreign keys in innodb
CREATE TABLE parent (
id INT NOT NULL,
PRIMARY KEY (id)
) ENGINE=INNODB;
CREATE TABLE child (
id INT,
parent_id INT,
INDEX par_ind (parent_id),
FOREIGN KEY (parent_id) REFERENCES parent(id) ON DELETE CASCADE
) ENGINE=INNODB;
In your example user is the same as parent (a user has many locations, a parent has many childs) and location is the same as child (a location has one user, a child has one parent)

What are MySQL foreign keys?

In an answer on Stack Overflow, I saw this code:
CREATE TABLE Favorites (
user_id INT NOT NULL,
movie_id INT NOT NULL,
PRIMARY KEY (user_id, movie_id),
FOREIGN KEY (user_id) REFERENCES Users(user_id),
FOREIGN KEY (movie_id) REFERENCES Movies(movie_id)
);
I've never used the 'foreign key' relationship keyword before.
What is it?
Why do people use it?
Does it provide any benefit apart from semantics?
A foreign key is a reference to a primary key in another table or the table itself. It is used for what is called referential integrity. Basically in your table provided, for a Record to be inserted into Favorites - you would have to supply a valid user_id from the Users table, and a valid movie_id from the Movies table. With Foreign keys enforces, I could not delete a record from Users or Movies. If I didn't have foreign keys, I could delete those records. Then if I did a SELECT ... JOIN on Favorites it would break.
See Wikipedia.
A foreign key describes a relationship between two tables. It has many benefits:
You can enforce that if a value is in the one table then it must also exist in the other table. Any attempt to break this constraint will give an error.
It allows database administrators or programmers to more easily understand the relationship between the tables just by looking at the table definitions.
It allows tools to inspect the schema and draw diagrams showing the relations between the tables, or create classes where the children of an object are accessible from the parent via members.
In InnoDB adding a foreign key also automatically adds an index on that column so that the join can be made efficiently in either direction. (Source)
If you are using MyISAM then foreign keys are not supported.
You can also add the ability to cascade when a related record is deleted. For example if I have a library table with many books in a related "book" table and I delete a given library from my library table its' dependent book records will also be deleted.