When I add a foreign key to a table that already has data, what does each of these database management systems do?
Do they analyze each value of the column to confirm it is a value from the referenced table primary key ?
Or do they have some other optimized mechanism ? And if that's the case, what is that mechanism ?
I can't confirm for MonetDB, but in PostgreSQL and MySQL (and most probably on MonetDB too) the answer is yes, they will check every value and will raise an error if the key does not exists on the referenced table.
Notice that the referenced column doesnt need to be the primary key for the referenced table - you can reference any column as a foreign key to the other table.
Yes, of course, a constraint that is not enforced would make no sense.
You can just try(this is for Postgres):
DROP SCHEMA tmp CASCADE;
CREATE SCHEMA tmp ;
SET search_path=tmp;
CREATE TABLE one
( one_id SERIAL NOT NULL PRIMARY KEY
, name varchar
);
INSERT INTO one(name)
SELECT 'name_' || gs::text
FROM generate_series(1,10) gs ;
CREATE TABLE two
( two_id SERIAL NOT NULL PRIMARY KEY
, one_id INTEGER -- REFERENCES one(one_id)
);
INSERT INTO two(one_id)
SELECT one_id
FROM one ;
DELETE FROM one WHERE one_id%5=0;
ALTER TABLE two
ADD FOREIGN KEY (one_id) REFERENCES one(one_id)
;
\d one
\d two
Result:
NOTICE: drop cascades to 2 other objects
DETAIL: drop cascades to table tmp.one
drop cascades to table tmp.two
DROP SCHEMA
CREATE SCHEMA
SET
CREATE TABLE
INSERT 0 10
CREATE TABLE
INSERT 0 10
DELETE 2
ERROR: insert or update on table "two" violates foreign key constraint "two_one_id_fkey"
DETAIL: Key (one_id)=(5) is not present in table "one".
Table "tmp.one"
Column | Type | Modifiers
--------+-------------------+------------------------------------------------------
one_id | integer | not null default nextval('one_one_id_seq'::regclass)
name | character varying |
Indexes:
"one_pkey" PRIMARY KEY, btree (one_id)
Table "tmp.two"
Column | Type | Modifiers
--------+---------+------------------------------------------------------
two_id | integer | not null default nextval('two_two_id_seq'::regclass)
one_id | integer |
Indexes:
"two_pkey" PRIMARY KEY, btree (two_id)
The error message is the same as for an actual insert or update. And you can see that the engine bails out once it en counters the first conflicting row.
Related
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 :)
I have two mysql tables. The first one is created using the following code:
create table project(
project_id int not null auto_increment,
project_name varchar(30)
primary key(project_id));
The second table:
create table matches(
match_id int not null auto_increment,
match_name varchar(30),
project_id int(4) foreign key (project_id) references projects(project_id));
Both these commands works fine. I want to add the project_name column from the first table to the second table. I tried using
alter table projects drop primary key, add primary key(project_id, project_name);
and then
alter table matches add column project_name varchar(30), add foreign key (project_name) references projects(project_name);
But got the following error:
ERROR 1005 (HY000): Can't create table 'matches.#sql-55e_311' (errno: 150)
How do i include both the columns from the first table into the second table.
The current structure of my second table is as follows:
+------------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+-------------+------+-----+---------+----------------+
| match_id | int(11) | NO | PRI | NULL | auto_increment |
| match_name | varchar(30) | YES | | NULL | |
| project_id | int(4) | NO | MUL | NULL | |
+------------+-------------+------+-----+---------+----------------+
I want to add the project_name as the fourth column in my second table.
To use a compound Primary Key as Foreign Key, you'll have to add the
same number of columns (that compose the PK) with same datatypes to
the child table and then use the combination of these columns in the
FOREIGN KEY definition.
see related post here https://stackoverflow.com/a/10566463/4904726
so try this way
alter table matches add foreign key (project_id, project_name) references projects(project_id, project_name);
Do you understand what a FK constraint says? It says a list of values for some columns in a table must appear as a list of values for some columns forming a (declared) PK or UNIQUE NOT NULL in the referenced table. So what you are writing doesn't make sense. If you wanted a PK (project_id, project_name) then that should also be the form of your FK.
But (project_id, project_name) is not two 1-column PKs, it is one 2-column PK, and it is probably not what you want since presumably in projects it isn't just pairs that are unique it is each column. Presumably you want two 1-column PKs and two one-column FKs, one referencing each PK.
If projects project_id was NOT NULL you could write:
alter table projects add primary key(project_name);
alter table matches add column project_name varchar(30),
add foreign key (project_name) references projects(project_name);
But if project_name can be NULL in projects then you cannot make it a PK and you cannot sensibly have a FK to it. You can make it UNIQUE. Unfortunately MySQL lets you write such a FK declaration to a non-NULL UNIQUE column while it also tells you not to do it:
The handling of foreign key references to non-unique keys or keys that contain NULL values is not well defined for operations such as UPDATE or DELETE CASCADE. You are advised to use foreign keys that reference only keys that are both UNIQUE (or PRIMARY) and NOT NULL.
So if you want projects project_name to be nullable then should declare it UNIQUE but you should enforce the logical matches project_name match with a trigger.
Trying to create this example table structure in Postgres 9.1:
CREATE TABLE foo (
name VARCHAR(256) PRIMARY KEY
);
CREATE TABLE bar (
pkey SERIAL PRIMARY KEY,
foo_fk VARCHAR(256) NOT NULL REFERENCES foo(name),
name VARCHAR(256) NOT NULL,
UNIQUE (foo_fk,name)
);
CREATE TABLE baz(
pkey SERIAL PRIMARY KEY,
bar_fk VARCHAR(256) NOT NULL REFERENCES bar(name),
name VARCHAR(256)
);
Running the above code produces an error, which does not make sense to me:
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "foo_pkey" for table "foo"
NOTICE: CREATE TABLE will create implicit sequence "bar_pkey_seq" for serial column "bar.pkey"
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "bar_pkey" for table "bar"
NOTICE: CREATE TABLE / UNIQUE will create implicit index "bar_foo_fk_name_key" for table "bar"
NOTICE: CREATE TABLE will create implicit sequence "baz_pkey_seq" for serial column "baz.pkey"
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "baz_pkey" for table "baz"
ERROR: there is no unique constraint matching given keys for referenced table "bar"
********** Error **********
ERROR: there is no unique constraint matching given keys for referenced table "bar"
SQL state: 42830
Can anyone explain why this error arises?
It's because the name column on the bar table does not have the UNIQUE constraint.
So imagine you have 2 rows on the bar table that contain the name 'ams' and you insert a row on baz with 'ams' on bar_fk, which row on bar would it be referring since there are two rows matching?
In postgresql all foreign keys must reference a unique key in the parent table, so in your bar table you must have a unique (name) index.
See also http://www.postgresql.org/docs/9.1/static/ddl-constraints.html#DDL-CONSTRAINTS-FK and specifically:
Finally, we should mention that a foreign key must reference columns
that either are a primary key or form a unique constraint.
Emphasis mine.
You should have name column as a unique constraint. here is a 3 lines of code to change your issues
First find out the primary key constraints by typing this code
\d table_name
you are shown like this at bottom "some_constraint" PRIMARY KEY, btree (column)
Drop the constraint:
ALTER TABLE table_name DROP CONSTRAINT some_constraint
Add a new primary key column with existing one:
ALTER TABLE table_name ADD CONSTRAINT some_constraint PRIMARY KEY(COLUMN_NAME1,COLUMN_NAME2);
That's All.
when you do UNIQUE as a table level constraint as you have done then what your defining is a bit like a composite primary key see ddl constraints, here is an extract
This specifies that the combination of values in the indicated columns is unique across the whole table, though any one of the columns need not be (and ordinarily isn't) unique.
this means that either field could possibly have a non unique value provided the combination is unique and this does not match your foreign key constraint.
most likely you want the constraint to be at column level. so rather then define them as table level constraints, 'append' UNIQUE to the end of the column definition like name VARCHAR(60) NOT NULL UNIQUE or specify indivdual table level constraints for each field.
Well, i have two tables
publica_evento_grupo
Field | Type | Null | Key | Default | Extra
id_evento_grupo int(10) unsigned NO PRI auto_increment
id_evento int(8) unsigned NO MUL
id_grupo int(8) unsigned NO MUL
identificacao varchar(55) NO
publica_identificacao_publicacao
Field | Type | Null | Key | Default | Extra
id_evento int(8) unsigned NO PRI
identificacao varchar(55) NO PRI
publica_evento_grupo.id_evento in is a foreign key to a third table called publica_evento but is also a foreign key together with the column to the table publica_identificacao_publicacao.identificacao. The problem is, I got to create the foreign key that relates publica_evento_grupo to publica_identificacao_publicacao by the key id_evento, but when I try to create the another FK with the column identificacao, it gives the errno below
[Err] 1005 - Can't create table '#sql-1049_1980992' (errno: 150).
As you can see the table publica_identificacao_publicacao has two PK and that iss the reason that it has to be 2 FK to relate them, i didn't create the indexes yet, because as far as i know, i just have to create the indexes after adding the FK contraints, and I was able to create the FK contraint to the column id_evento between evento_grupo and identificacao_publicacao, i don't know why just the column identificacao is giving this error
EDIT 1: #RolandBouman i don't have the permission to use that command SHOW ENGINE INNODB STATUS
EDIT 2: #Geoff_Montee acctually the command you passed worked, to undestand why i use that structure take a look at this question MySQL UNIQUE Constraint multiple columns condition
Without the actuall DDL it's not so easy to say. I recommend doing:
SHOW ENGINE INNODB STATUS;
Immediately after you run into this error. In the output, look for a section that looks like:
------------------------
LATEST FOREIGN KEY ERROR
------------------------
121026 22:40:18 Error in foreign key constraint of table test/#sql-154c_94:
foreign key(rid) references bla(id):
Cannot find an index in the referenced table where the
referenced columns appear as the first columns, or column types
in the table and the referenced table do not match for constraint.
Note that the internal storage type of ENUM and SET changed in
tables created with >= InnoDB-4.1.12, and such columns in old tables
cannot be referenced by such columns in new tables.
See http://dev.mysql.com/doc/refman/5.5/en/innodb-foreign-key-constraints.html
for correct foreign key definition.
You'll find detailed info there about what's wrong with your foreign key definition.
That error usually happens because there is a type mismatch between the referring table and the referred table. In this case, the types seem to match.
Have you considered having a constraint for both fields in the primary composite key? You might have to remove the existing foreign key constraint on id_evento first.
ALTER TABLE publica_evento_grupo ADD FOREIGN KEY
(id_evento, identificacao) REFERENCES
publica_identificacao_publicacao (id_evento, identificacao);
It seems as though the error in this case might be because you try to add a foreign key constraint to only part of a composite primary key.
Does identificacao exist in any other tables? Why do you want to add a foreign key constraint to only part of a composite primary key?
Think about it this way...
Let's say you do have a foreign key constraint in the publica_evento_grupo table to the identificacao field by itself. Let's also say in the publica_identificacao_publicacao table you have (1, "some string") and (2, "some string"). So if you delete (2, "some string"), but leave (1, "some string") in its place. The rows that refer to (1, "some string") in the publica_evento_grupo table will complain, even though they shouldn't!
So it sounds to me like you should add a foreign key constraint to all of a primary key or none of it.
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.