how to use sql foreign keys - mysql

After googling about foreign keys, this is the way i understood them.
If i need to save the phone-no of people in a table with address, it will create multiple records for a single person as he can have multiple numbers. that will also store redundant address values in each repeated row. so using the user-id as foreign key, i can phone-no to another table and save the address from being repeated. So my question is if a user is deleted from the user table with address, will it also automatically remove all associated values in phone table? Or that has to be specified at the time of creating the table that deleting this will delete that? also what happens if user-id is changed only in first table and when it is changed in only 2nd table.
If i have 2 tables
table: user
+-----------------------------+
|user-id | username | address |
+-----------------------------+
table: phone-no
+--------------------------+
| pid | phone-no | user-id |
+---------------------------
Also, if it is not too much, can you show me the query for creating both of this with foreign keys.

User-id from table user would be the primary key, and user-id in the table phone-no would be the foregin key referencing user-id in user table.
create table user(
userid int identity(1,1) not null primary key,
username varchar(50) not null,
adress varchar(200) not null,
);
create table phone-no(
pid int identity(1,1) not null primary key,
phone-no int not null,
user-id int not null foreign key (userid) references user(userid) on delete cascade,
);
On delete cascade option is optional, hope you understand what i meant by that.

Your design is correct. Here's MySQL documentation for foreign key syntax, for your table, it will look something like this:
CONSTRAINT fk_user_id FOREIGN KEY (user-id)
REFERENCES user(user-id)
ON DELETE CASCADE
Here, ON DELETE CASCADE will make sure the phone record gets deleted when user is deleted. If you want to perform a different action, (e.g. set user-id to null and don't delete record), you can use SET NULL. Have a look at Referential Actions section of the above documentation.

Related

How to drop a column with foreign key in MySQL?

Let's say I used the following structure to create a table:
CREATE TABLE staff (
sid INT AUTO_INCREMENT,
sfname VARCHAR(30) NOT NULL,
slname VARCHAR(30) NOT NULL,
uid int UNIQUE,
bid int NOT NULL,
PRIMARY KEY (sid),
FOREIGN KEY (uid) REFERENCES sajilo_user(uid),
FOREIGN KEY (bid) REFERENCES branch(bid)
);
Now I want to add DELETE CASCADE for the foreign key and I found that I can achieve it by dropping the column(column with foreign key) and adding it again with DELETE CASCADE property on alter table statement.
But when I tried:
ALTER TABLE staff DROP column uid;
I got error: #1553 - Cannot drop index 'u_user': needed in a foreign key constraint.
So I need to remove foreign key constraint first with:
ALTER TABLE staff DROP FOREIGN KEY foreign_key_constraint_name;
As you saw above table was created without giving the name for constraint. I am having trouble to drop it. So what should be done ?
I need the way of dropping the column along with foreign key constraint:)
Finally, I found the solution, We can use information_schema to retrieve the name of a foreign key and can use the name event constraint name is not set explicitly as shown above.
As of MySQL docs:
INFORMATION_SCHEMA provides access to database metadata, information about the MySQL server such as the name of a database or table, the data type of a column, or access privileges. Other terms that are sometimes used for this information are data dictionary and system catalog.
For more information visit: https://dev.mysql.com/doc/refman/8.0/en/information-schema.html
So we can do something like:
USE information_schema;
SELECT * FROM `INNODB_SYS_FOREIGN_COLS`;
Where INNODB denotes a storage engine, for more information visit:
https://dev.mysql.com/doc/refman/8.0/en/storage-engines.html
After executing the query we can get the result something like:
ID FOR_COL_NAME REF_COL_NAME POS
---------------------------------- ------------ ------------ --------
acdb/takes_ibfk_1 sid sid 0
acdb/takes_ibfk_2 cid cid 0
sajilo_courier/admin_branch_ibfk_1 aid aid 0
sajilo_courier/admin_branch_ibfk_2 bid bid 0
sajilo_courier/admin_ibfk_1 uid uid 0
sajilo_courier/admin_staff_ibfk_1 aid aid 0
sajilo_courier/admin_staff_ibfk_2 sid sid 0
sajilo_courier/staff_ibfk_1 uid uid 0
As we can see in the ID contains the name of database/foreign key name so finally we can do something like
ALTER TABLE staff DROP FOREIGN KEY staff_ibfk_1 ;
From last row and finally we can drop the column easily :)

is it possible to add a condition to foreign key constrain in phpmyadmin?

I have two tables: Message and Product.
In the Message table, there is a sourceid, which is equal to ProductId under table Product.
A Product is given a product id (productID), and a user can message the seller regarding the product. And for those messages, if they are related to certain product than they are given a sourceid, which is basically the product id. If the message is not related to any product, the sourceid will be set as 0.
I am able to set a foreign key constrain to delete the message entry whenever a seller delete a product, all related messages will be deleted.
However, I found that all message that is not related to a product, with sourceid set as 0, are not saving in the database. Obviously, because there is not a product with a productid = 0, and that the foreign key constraint will delete the message entry (with sourceid = 0) immediately.
Is there a way to set condition in phpmyadmin? If I can set that if the foreign key constraint only execute when sourceid > 0, then the system should work out. But I am not sure how to set this condition, or if phpmyadmin allow to set this condition.
Please help. Thank you so much.
Yes, if the foreign key is not relevant to some messages, make the product_id foreign key column be nullable. Store NULL instead of 0 when you don't want to reference any product. The NULL is ignored for purposes of the foreign key reference.
This is virtually the same as these past questions I answered:
MySQL foreign key to allow NULL? (2009)
Foreign key or null value (2011)
You can define the Message table with a foreign key using ON DELETE CASCADE:
CREATE TABLE Message (
messageid INT(11) NOT NULL AUTO_INCREMENT,
message VARCHAR(255) NOT NULL,
-- other columns
sourceid INT(11) NOT NULL,
PRIMARY KEY (messageid),
FOREIGN KEY (sourceid) REFERENCES Product(ProductId) ON DELETE CASCADE
) ENGINE=InnoDB;
If you don't want to redefine the entire table, you can use the following ALTER TABLE statement:
ALTER TABLE Message
ADD CONSTRAINT fk_pid
FOREIGN KEY (sourceid)
REFERENCES Product (ProductId)
ON DELETE CASCADE
With this change in place, deleting a product record from the Product table will cause all its corresponding messages in Message to also be deleted.

Referenced table is unexceptionally droppable

When I use this query:
CREATE TABLE users(
id int not null auto_increment primary key,
username varchar(30) not null unique,
email varchar(255) not null unique,
password varchar(255) not null
);
CREATE TABLE items(
id int not null auto_increment primary key,
name varchar(30) not null,
user_id int not null,
FOREIGN KEY user_key(user_id)
REFERENCES users(id)
);
DROP TABLE users;
It shows this error:
1217 - Cannot delete or update a parent row: a foreign key constraint fails
Which is alright because that is how mySQL database naturally reacts when we want to drop table that is referenced by other table that depends on it.
However, this same query shows no errors and actually drops users table on my pal's PC.
What could be the case? Is there a way to disable it?
You may be using different database engines. MyISAM and InnoDB have different FK support/enforcement, I believe. It could also be that the data in each of your tables is different.
If you want to drop a table that is a dependency of another table, though, the "right" way is to remove the FK from the dependent table and then drop the table that you want to.

MariaDB foreign key issue

Been awhile since I've had to create/define a database, so ignore my lack of understanding. Basically, I have a table that has a few fields, one of which is a hash which references another table with the same hash, and the hash value. I believe the problem lies in the fact that I'm using the foreign key to references the non-primary key in the secondary table. So my question is this: how do I set up this relationship? Below is a minimum example to create my issue:
-- first table, imagine this as the hash value (should I remove the r_id,
-- and make the r_hash a unique & primary key?)
create table rx(
r_id int(10) auto_increment primary key,
r_hash varchar(175) default null,
r_val varchar(175) default null
);
create table cx(
c_id int(10) auto_increment primary key,
c_name varchar(175) default null,
querystr varchar(175) default null,
r_c_hash varchar(175) default null,
constraint r_fk foreign key(r_c_hash) references rx(r_hash)
);
And the classic error that I somehow still can't get around:
ERROR 1005 (HY000): Can't create table 'test2.cx' (errno: 150)
EDIT:
Just for clarification, an example row from rx will look like this:
1 | asdkjIOFJE93fijflskf | anexamplehashvalue
and an example row from cx may look like this:
1 | name_of_file | queryString=yes&1=3 | asdkjIOFJE93fijflskf
which as you can see, the hash values match which is all I need.. Do I recreate the rx table with the hash as the PK and make it unique? or can I keep the structure of the tables as it already is?
Why are you not referencing the primary key? That really is what primary keys are for.
If you really, really need to use a different key, then you should define a unique index on it:
create unique index unq_rx_r_hash on rx(r_hash);

Working with foreign keys - cannot insert

Doing my first tryouts with foreign keys in a mySQL database and are trying to do a insert, that fails for this reason: Integrity constraint violation: 1452 Cannot add or update a child row: a foreign key constraint fails
Does this mean that foreign keys restrict INSERTS as well as DELETES and/or UPDATES on each table that is enforced with foreign keys relations?
Thanks!
Updated description:
Products
----------------------------
id | type
----------------------------
0 | 0
1 | 3
ProductsToCategories
----------------------------
productid | categoryid
----------------------------
0 | 0
1 | 1
Product table has following structure
CREATE TABLE IF NOT EXISTS `alpha`.`products` (
`id` MEDIUMINT UNSIGNED NOT NULL AUTO_INCREMENT ,
`type` TINYINT(2) UNSIGNED NOT NULL DEFAULT 0 ,
PRIMARY KEY (`id`) ,
CONSTRAINT `funkyfunky`
FOREIGN KEY (`id` )
REFERENCES `alpha`.`ProductsToCategories` (`productid` )
ON DELETE CASCADE,
ON UPDATE CASCADE)
ENGINE = InnoDB;
Your insert is failing because the foreign key in the row you are inserting doesn't match a valid key in the constraint table. For example:
Assume you've got these two tables:
Employees
----------------------------
EmpID | Name
----------------------------
0 | John
1 | Jane
OfficeAssignments
----------------------------
OfficeID | EmpID
----------------------------
0 | 0
1 | 1
If you have a foreign key constraint on OfficeAssignments.EmpID -> Employees.EmpID, and you try to execute:
INSERT INTO OfficeAssignments (OfficeID, EmpID) VALUES (2,2)
The statement will fail because there is no entry in the Employees table with an EmpID of 2.
Constraints are designed to ensure that your dependent table always has valid data with regard to the parent table -- in this example, you will never have an office which is listed as assigned to an employee who doesn't exist in the system, either because they never existed (as in this case) or because they've been deleted (because the constraint will prevent the employee record from being deleted until the office assignment record has been deleted first).
Edit: Now that you've posted the constraint, it indeed looks like it might be set up backwards. By placing the constraint in the definition of the Products table, you are making it the child, and ProductsToCategories the parent. The constraint you've written can be read as, "a Product must be assigned to a category before it can be created". I suspect what you meant is the other way around: "a Product must be created before it can be assigned to a category." To get that result, you need to place the constraint on the ProductsToCategories table, setting the foreign key to productid and referencing Products.id.
You cannot delete a row from the parent table while there is a foreign key reference to it from a child table. Also you cannot insert/update in the child table with invalid id's in the foreign key column.
Edit: The "CONSTRAINT funkyfunky FOREIGN KEY (id)" must be declared in the "ProductsToCategories" table not in the "Products" table, because "ProductsToCategories" is referencing "Products" not the opposite as you have did.
Your products table is slightly wrong, as you don't need to reference anything from it. References go in the "other" tables, and point to the main, e.g:
create table products (
id int auto_increment,
type int,
primary key (id)
);
create table categories (
id int auto_increment,
name varchar(128),
primary key (id)
)
create table products_to_categories (
product_id int references products,
category_id int references categories
);
A foreign key enforces a valid relation between the rows in two tables. In order to be able to insert a row into a table containing a foreign key, there must be a row in the referenced table containing that key or the insert will fail. The same with delete, you can't delete the row in the referenced table while there are still rows in the table with the foreign key that still reference it. The prevents ending up with rows in the dependent table that have data, but don't have associated rows in the referenced table, i.e., a violation of referential integrity.