MySQL on delete cascade, FK in source table - mysql

I have 2 tables, customer and address. Customer has column address with FK referencing to table address.
Table customer
create table customer
(
id int(11) not null primary key,
...
address int(11) null,
constraint fk_customer_address
foreign key (address) references address (id)
on update cascade on delete cascade,
)
Table address
create table address
(
id int(11) not null primary key,
...
)
I want to have address id in customer table, because in future there will be more tables referencing to address table (Users.address, Business.address, etc) and one table for all addresses because of the same format.
When I delete row in customer table, address row stays in DB.
Is it possible to use this FK relation to delete row in address table, or do I have to delete it manually (programmatically)?
Does this mean that when I delete row from address, customer row is deleted as well?
Edit:
Yes, it does.

You probably don't want customer rows deleted when an address row is deleted. I think the fk would be correct if it was defined as "on delete restrict" or "on delete set null".
So that if you wanted to delete the address record:
on delete set null ➞ All references to that address would be set as null
on delete restrict ➞ You should manually update the address_id it on ALL child tables to some other value (or null) and then delete the parent row
You could somehow avoid the programatic deletion by defining the fk the opposite (that is: address table has a foreign key to customer table, and others)
However, for me this feels somewhat unnatural since you would have to add a new column on the address table for each other table you want to reference. It feels more natural for each table to have the fk of the address.
Another drawback is that it would not allow you to reuse same address on the same table. For example, if two customers would share the same address, you would need to create a row for each of those customers.
With the first solution you would just need a single row on the address table, that could be referenced multiple times on the customer table by different customers.
Of course, your requirements may be a 1:1 relationship. But remember that requirements can change :). And if they do, and you later require to share same address between different rows, you are ready to go with the first solution.
I would use your original approach with a "on delete set null" constraint.

Related

How to prevent orphaned polymorphic records?

I have a database of polymorphic structure: a "base" type table and two "derived" types:
CREATE TABLE ContactMethod(
id integer PRIMARY KEY
person_id integer
priority integer
allow_solicitation boolean
FOREIGN KEY(person_id) REFERENCES People(id)
)
CREATE TABLE PhoneNumbers(
contact_method_id integer PRIMARY KEY
phone_number varchar
FOREIGN KEY(contact_method_id) REFERENCES ContactMethod(id)
)
CREATE TABLE EmailAddresses(
contact_method_id integer PRIMARY KEY
email_address varchar
FOREIGN KEY(contact_method_id) REFERENCES ContactMethod(id)
)
I want to prevent orphaned ContactMethod records from existing, that is, a ContactMethod record with neither a corresponding PhoneNumber record nor an EmailAddress record. I've seen techniques for ensuring exclusivity (preventing a ContactMethod record with both a related PhoneNumber and EmailAddress), but not for preventing orphans.
One idea is a CHECK constraint that executes a custom function that executes queries. However, executing queries via functions in CHECK constraints is a bad idea.
Another idea is a View that will trigger a violation if an orphaned ContactMethod record is added. The "obvious" way to do this is to put a constraint on the View, but that's not allowed. So it has to be some sort of trick, probably involving an index on the View. Is that really the best (only?) way to enforce no orphans? If so, what is a working example?
Are there other ways? I could get rid of ContactMethod table and duplicate shared columns on the other two tables, but I don't want to do that. I'm primarily curious about capabilities available in MySQL and SQLite, but a solution in any SQL engine would be helpful.
The simplest solution would be to use single table inheritance. So both the contact methods are optional (that is, nullable) fields in the ContactMethod table, but you add a CHECK constraint to ensure at least one of these has a non-null value.
CREATE TABLE ContactMethod(
id integer PRIMARY KEY
person_id integer
priority integer
allow_solicitation boolean,
phone_number varchar DEFAULT NULL
email_address varchar DEFAULT NULL
FOREIGN KEY(person_id) REFERENCES People(id)
CHECK (COALESCE(phone_number, email_address) IS NOT NULL)
)
Another solution that supports polymorphic associations is to reverse the direction of foreign key. Make ContactMethod have a one nullable foreign key for each type of associated method. Use a CHECK to make sure at least one has a non-null value. This works because you don't allow multiple emails or phones per row in ContactMethod. It does mean if you add a different type of contact (e.g. Signal account), then you'd have to add another foreign key to this table.
CREATE TABLE ContactMethod(
id integer PRIMARY KEY
person_id integer
priority integer
allow_solicitation boolean,
phone_number_id integer DEFAULT NULL
email_address_id integer DEFAULT NULL
FOREIGN KEY(person_id) REFERENCES People(id)
FOREIGN KEY(phone_number_id) REFERENCES PhoneNumbers(id)
FOREIGN KEY(email_address_id) REFERENCES EmailAddresses(id)
CHECK (COALESCE(phone_number_id, email_address_id) IS NOT NULL)
)
A newly inserted ContactMethod will always be orphaned until you insert a phone number or an e-mail address. So, you cannot test the condition at insert.
Instead, you could insert contact information with a stored procedure having an optional phone number and optional e-mail parameter in addition to the base information. The base record would only be inserted if at least one of the two has a non-null value.
Then create a delete trigger when a phone number or an e-mail address is deleted, to either delete the ContactMethod record when no related record exist anymore or to raise an exception as shown in Alter a Delete Trigger to Check a Column Value

In a one to one or one to many relationship in a database, which table must have the foreign key?

Sorry If my question seems too obvious but I still can't find the logic between a relationship of a database model.
ONE TO ONE:
If I have a customer that has only one address where do I have to put my foreign key ?
I first thought it would be a good idea to put it on the customer's table; one column with a foreign key referring to an id of an address.
Because if I need to create a customer, first I would need to create an address.
But I have found some example on the internet
, where they put a foreign key in the address' table, referring to an id of a customer
ONE TO MANY :
And the same question would apply if a customer can have multiple addresses, in which table, should I put my foreign key ?
The foreign key goes on the "many" side.
For example, if a sales_order is associated with at most one customer, and a customer can have zero, one or more sales_order
Then we put customer_id in the sales_order table, as a reference to the (unique) id column in customer table.
This means that we will first need to add a row to customer before we can add a sales_order for that customer. (The foreign key constraint will prevent us from adding a row in sales_order that has a value in customer_id column that doesn't exist in id column of customer.
--
For the special case of a one-to-one relationship, we can implement that the same way as a one-to-many, with an additional UNIQUE constraint on the foreign key column. It really comes down to deciding which direction of the relationship is mandatory, and which is optional. Basically, which table will we add a row to first? The table where we add rows later will have the foreign key constraint referencing the table that we previously added a row...

Where should the foreign reside in this scenario

I want the relationship to be like a single profile only have one address associated to it. While a single address can be from many profile. Where would I place the foreign key.
Address address_id, state, town, phone, email
Profile username, profile_pic, date_added, password
well since you want one to many relation for address table you should add a foreign key ( let's say addr_id ) to profile table.
That way one address row can be used in multiple profiles
From http://www.mysqltutorial.org/mysql-foreign-key/:
"The customers table is called parent table or referenced table, and the orders table is known as child table or referencing table.
A foreign key can be a column or a set of columns. The columns in the child table often refer to the primary key columns in the parent table."
Looks like you want the foreign key on the user table.
Hope this helps!

Create one to one relationship in MySQL

Is this the correct code to link two tables in a way that one-to-one Relationship in MySQL?
Table1
CREATE TABLE employees (id INT PRIMARY KEY AUTO_INCREMENT,FullName VARCHAR(50))
Table2
CREATE TABLE salary (id INT PRIMARY KEY AUTO_INCREMENT,SalaryNumber VARCHAR(6))
ALTER TABLE salary
ADD FOREIGN KEY (id) REFERENCES employees (id)
ON DELETE CASCADE
ON UPDATE CASCADE
Your example has a number of problems, first of which is that you're linking two non-related auto-incrementing ids. This is a nightmare waiting to happen. If anything happens to get those two ids out of sync, you're dead in the water.
In your example, the 'employees' record would be considered the parent record, upon which the 'salary' record is dependent (i.e. - you might have an employee record without a corresponding salary record, but you wouldn't want a salary record that's not associated with an employee).
Foreign key constraints belong in the child table, as stated in the MySql documentation. As such, what you need in the 'salary' table is a column that looks like this:
EmployeeId INT NOT NULL
Your foreign key would be
ALTER TABLE salary
ADD FOREIGN KEY (EmployeeId) REFERENCES employee (id)
ON DELETE CASCADE
ON UPDATE CASCADE
At this point, you still have a many-to-one relationship, as there is nothing to prevent you from inserting multiple entries into the salary table with the same EmployeeId.
To make this relationship one-to-one, you have to create a unique index on the salary.EmployeeId column.
With this type of relationship, it's important to note that:
you are prevented from inserting a row into salary that doesn't have valid EmployeeId
you are prevented from inserting a row into salary that has a duplicate EmployeeId
when you delete a salary record, the employee record remains untouched.
deleting the employee record results in the deletion of the salary record it is referencing (if you don't want this behavior, change ON DELETE CASCADE to something else)
you are prevented from dropping the employee table until the salary table is empty and dropped.
No, that's a one-to-many relationship. You can have any number of salary rows (including none) linking to a single employee row.
Since the salary is very much an attribute dependent on the employee, I would consider placing it in the employee table itself (unless you have some extra knowledge you haven't shared with us which makes this problematic).
There are ways to enforce one-to-one across separate tables.
For example, you can use triggers to stop duplicates, though some people are adverse to triggers, and this still won't prevent one-to-zero mappings from appearing.
Alternatively, you can use bidirectional foreign keys with a dummy row in both tables (pointing to each other) to allow you to insert in one table at a time.
The way this is done is to insert a row in employees pointing to the dummy salary row.
Then insert the salary row pointing to the newly inserted employee.
Then update the employee row to pint to the newly inserted salary row. All this should happen as a single transaction of course, to maintain referential integrity at the application level.
In order to get the dummy rows in, they'll need to be inserted before the foreign key constraints are added to the schema, otherwise you have a chicken-and-egg situation.
Whether that level of work is actually necessary is debatable, especially when you can enforce one-to-one simply by combining the data into a single table as suggested :-)

MySql Foreign keys: ON DELETE NO ACTION behavour - how to leave info in referenced field?

I have two tables, one 'users' and one 'orders', where the order table has a foreign key reference to the user id table. (I'm using email address as user id in this case.)
What I want to accomplish is this:
If the user table id is updated (= email address is changed), this will reflect in the referenced order table user_id field. (This works fine using ON UPDATE CASCADE directive - no problem here!)
If the user is deleted from the user table, the order will remain, KEEPING the referenced user_id.
I'm having problem with the second goal:
If I use ON DELETE CASCADE on the referenced user id field, the order row is of course deleted.
If I use ON DELETE NO ACTION, I get an error when I try to remove the user. (#1451 - Cannot delete or update a parent row: a foreign key constraint fails)
If I use ON DELETE SET NULL, I can delete the user row, but the referenced info is set to null so I can't track down who made that order...
Is there a way to allow removal of the user from the user table while keeping the referenced user_id information in the orders table?
Two options:
Don't actually delete the user, just mark them as deleted by setting a deleted field.
or:
Remove the foreign key constraint.
I would recommend the first option. Taking the second option can lead to data inconsistencies. Besides, just having the value of the key without the corresponding data in the other table will not help you much anyway.
Actually there is another alternative - replace the user table's email address key with an autoincrement INT.
Then you could just copy the user attribute (ugh, denormalized) into the order (I guess you could justify it as being the 'email address of the ordering user at the time of order').
Then ON DELETE SET NULL could reset the foreign key INT but not the copied attribute (email address).