I am in the middle of migrating an old (unnormalized) database to its new version.
Right now I have this intermediate result:
CREATE TABLE recipient(
id int NOT NULL AUTO_INCREMENT,
email VARCHAR(255),
PRIMARY KEY (id),
UNIQUE INDEX (`email`),
) ENGINE=INNODB;
CREATE TABLE comment(
# Right now this is always NULL:
fk_recipient INT,
# Temporary solution. Note that this field is NOT UNIQUE:
tmp_email VARCHAR(255) NOT NULL,
# ...
FOREIGN KEY (`fk_recipient`) REFERENCES recipient(id);
) ENGINE=INNODB;
Both tables are filled with correct data:
some million comments with the right tmp_email and fk_recipient = null in table comment (note: emails are not unique)
some hundred thousand UNIQUE email adresses in table recipient.
What I need to do:
I want to get rid of the comment.tmp_email column and instead point comment.fk_recipient to the appropriate row in table recipient.
My current approach (using PHP):
get all comments
iterate over all comments:
look up the right row in recipient table
set right foreign key
... DROP COLUMN tmp_email
This takes forever and made me wonder if there is no native MySQL way to do that?
The following workaround will do the job:
Create temporary foreign key on comment.tmp_email:
ALTER TABLE comment
ADD CONSTRAINT `tmpMailKey` FOREIGN KEY (`tmp_email`)
REFERENCES `recipient`(`email`);
Join the two tables on the temporary key and use the information to set the real foreign key:
UPDATE comment c
INNER JOIN recipient r
ON
c.tmp_email = r.email
AND c.tmp_email IS NOT NULL
SET c.fk_recipient = r.id;
Get rid of temporary foreign key (and the tmp column too):
ALTER TABLE `udw_v3`.`travelogue_guestbookentry`
DROP COLUMN `tmp_email`,
DROP INDEX `tmpMailKey`,
DROP FOREIGN KEY `tmpMailKey`;
Related
I am creating web-app for database management. Database can be created using diagrams ER.
Here is screen from my app:
As you can see this pseudo example shows 4x types of cases:
1) Primary key --> Primary key (1:1)
2) Unique key --> Unique key (1:1)
3) Primary key consisting of two fields --> Primary key consisting of two fields (1:1)
4) Unique key consisting of two fields --> Unique key consisting of two fields (1:1)
And here is my question:
Is it all true? I wonder about these double keys... Is this really a 1 to 1 relation?
Generally, I wonder about these first 2 cases too. Are there also true?
MySQL Workbench shows it is not true:
I dont know why but you can see MySQL Workbench shows this is one to many relation...
Oracle Sql Developer:
Can anyone tell me when 1 to 1 relationship actually is?
Documentation shows i have right:
https://docs.oracle.com/cd/E26180_01/Platform.94/RepositoryGuide/html/s1204onetoonewithauxiliarytable01.html
but diagrams ER in MySQL Workbench and Sql Developer shows something different...
SQL code from that tables:
CREATE USER "Student" IDENTIFIED BY "null";
CREATE TABLE "Student".Table1 (
PK_FK NUMBER NOT NULL
);
CREATE TABLE "Student".Table2 (
PK NUMBER NOT NULL
);
CREATE TABLE "Student".Table3 (
PK NUMBER NOT NULL,
UK_FK NUMBER
);
CREATE TABLE "Student".Table4 (
PK NUMBER NOT NULL,
UK NUMBER
);
CREATE TABLE "Student".Table5 (
PK_1_FK NUMBER NOT NULL,
PK_2_FK NUMBER NOT NULL
);
CREATE TABLE "Student".Table6 (
PK_1 NUMBER NOT NULL,
PK_2 NUMBER NOT NULL
);
CREATE TABLE "Student".Table7 (
UK_1_FK NUMBER,
UK_2_FK NUMBER
);
CREATE TABLE "Student".Table8 (
UK_1 NUMBER,
UK_2 NUMBER
);
ALTER TABLE "Student".Table1 ADD CONSTRAINT Table1_PK PRIMARY KEY (PK_FK);
ALTER TABLE "Student".Table2 ADD CONSTRAINT Table2_PK PRIMARY KEY (PK);
ALTER TABLE "Student".Table3 ADD CONSTRAINT Table3_PK PRIMARY KEY (PK);
ALTER TABLE "Student".Table4 ADD CONSTRAINT Table4_PK PRIMARY KEY (PK);
ALTER TABLE "Student".Table5 ADD CONSTRAINT Table5_PK PRIMARY KEY (PK_1_FK, PK_2_FK);
ALTER TABLE "Student".Table6 ADD CONSTRAINT Table6_PK PRIMARY KEY (PK_1, PK_2);
ALTER TABLE "Student".Table3 ADD CONSTRAINT Table3_UK1 UNIQUE (UK_FK);
ALTER TABLE "Student".Table4 ADD CONSTRAINT Table4_UK2 UNIQUE (UK);
ALTER TABLE "Student".Table7 ADD CONSTRAINT Table7_UK3 UNIQUE (UK_1_FK, UK_2_FK);
ALTER TABLE "Student".Table8 ADD CONSTRAINT Table8_UK4 UNIQUE (UK_1, UK_2);
ALTER TABLE "Student".Table1 ADD CONSTRAINT Table1_FK1 FOREIGN KEY (PK_FK)
REFERENCES "Student".Table2 (PK);
ALTER TABLE "Student".Table3 ADD CONSTRAINT Table3_FK2 FOREIGN KEY (UK_FK)
REFERENCES "Student".Table4 (UK);
ALTER TABLE "Student".Table5 ADD CONSTRAINT Table5_FK3 FOREIGN KEY (PK_1_FK, PK_2_FK)
REFERENCES "Student".Table6 (PK_1, PK_2);
ALTER TABLE "Student".Table7 ADD CONSTRAINT Table7_FK4 FOREIGN KEY (UK_1_FK, UK_2_FK)
REFERENCES "Student".Table8 (UK_1, UK_2);
That's perfectly possible. Here's an example for PostgreSQL:
create table t1 (
a int not null,
b int not null,
constraint uq1 (a, b),
constraint fk1 foreign key (a, b) references t2 (a, b)
deferrable initially deferred
);
create table t2 (
a int not null,
b int not null,
constraint uq2 (a, b),
constraint fk2 foreign key (a, b) references t1 (a, b)
deferrable initially deferred
);
In this case t1 (a,b) is unique and references t2 (a, b) that is also unique. That's a 1:1 relationship using "composite keys".
Note: This example uses "circular references" that is a standard part of SQL, but is only implemented [to my knowledge] by PostgreSQL and Oracle. It won't run in MySQL.
A one-to-one relationship is still a master-detail relationship. One table is the owner of the identifier and the other table references it through a foreign key. This is the relationship show in the MySQL Workbench and SQL Developer pictures.
Documentation shows i have right:
You link to Oracle's documentation for ATG Repository, which is a specialist tool for representing data generically, but even there we can see from the SQL that USER_TBL is the primary table and "owns" the ID column and JOB_TBL is the auxiliary table and references the ID.
CREATE TABLE usr_tbl (
id VARCHAR(32) not null,
nam_col VARCHAR(32) null,
age_col INTEGER null,
primary key(id)
);
CREATE TABLE job_tbl (
id VARCHAR(32) not null references usr_tbl(id),
function VARCHAR(32) null,
title VARCHAR(32) null,
primary key(id)
In other words, we can have a USER without a JOB but we can't have a JOB without a USER. But a USER can have only one JOB and one JOB belongs only to ONE user.
Your diagram is wrong because it renders TABLE7 and TABLE8 as peers. But foreign keys don't work like that. One table defer to the other. When I look at your notation I can't see whether TABLE8 owns TABLE7 or TABLE7 owns TABLE8. Whereas, it's quite clear in the MySQL and Oracle diagrams. The purpose of a data model is to clarify the database design not obfuscate it.
Note, it is perfectly possible to define two tables which have foreign keys that reference each other's primary key. The trick is insert data into them. This requires deferring the foreign key constraints. I view deferred constraints as a red flag, a sign of a broken data model.
i have a Problem. I created a table in MySQL with a Primary key and two foreign keys.
The two foreign keys C and R have to be unique as pair. For the Moment this works.
But then i decided adding a 3rd key CR_New because of a new System feature. Now a combination of C_ID, R_ID and CR_New has to be unique. But it doesn´t work, even with the Solutions i already found in the Internet. I get error 1553 when dropping the foreign keys. A combination of the keys C_ID and R_ID is allowed multiple times if CR_New is different.
CREATE TABLE CR(
CR_ID INT UNSIGNED NOT NULL AUTO_INCREMENT,
CR_Grade TINYINT NOT NULL DEFAULT 0,
C_ID INT UNSIGNED NOT NULL,
R_ID INT UNSIGNED NOT NULL,
PRIMARY KEY(CR_ID),
FOREIGN KEY(C_ID) REFERENCES C(C_ID),
FOREIGN KEY(R_ID) REFERENCES R(R_ID),
CONSTRAINT UNIQUE(R_ID, C_ID));
ALTER TABLE CR ADD COLUMN CR_New INT;
UPDATE CR SET CR_New = 42;
ALTER TABLE CR ADD CONSTRAINT UNIQUE (C_ID, R_ID, CR_New);
ALTER TABLE CR DROP FOREIGN KEY C_ID;
ALTER TABLE CR DROP FOREIGN KEY R_ID;
ALTER TABLE CR DROP INDEX C_ID;
ALTER TABLE CR DROP INDEX R_ID;
If you want to know which use i want to have from it: Just imagine C is a Student, R a subject and CR_New the year, and CR_Grade the grade of the Student. Now it is just possible to save the grade for a subject, but i want to extend it for saving the grade for a subject for each year.
BTW thats not the original use but works the same way.
Now I solved the Problem by creating new tables. After filling them with my data I renamed them and now it works. Should work fine for a test-database :-)
I have two tables as follow:
1st Table:
CREATE TABLE User (
User_ID VARCHAR(8)NOT NULL PRIMARY KEY,
User_Name VARCHAR (25) NOT NULL,
User_Gender CHAR (1) NOT NULL,
User_Position VARCHAR (10) NOT NULL,
);
2nd table:
CREATE TABLE Training (
Training_Code VARCHAR(8) NOT NULL Primary Key,
Training_Title VARCHAR(30) NOT NULL,
);
I am trying to create a table which has two foreign keys to join both of the previous tables:
CREATE TABLE Request (
User_ID VARCHAR(8) NOT NULL,
Training_Code VARCHAR(8) NOT NULL,
Request_Status INT(1) NOT NULL
);
When I am trying to set the foreign keys in the new table, the User_ID can be done successfully but the Training_Code cannot be set to foreign key due to the error:
ERROR 1215 (HY000): Cannot add foreign key constraint
As I searched for this problem, the reason for it, is that data type is not the same, or name is not the same.. but in my situation both are correct so could you tell me what is wrong here ?
You need an index for this column in table Request too:
Issue first
CREATE INDEX idx_training_code ON Request (Training_Code);
Then you should be successful creating the foreign key constraint with
ALTER TABLE Request
ADD CONSTRAINT FOREIGN KEY idx_training_code (Training_Code)
REFERENCES Training(Training_Code);
It worked for me. But I've got to say that it worked without create index too, as the documentation of Using FOREIGN KEY Constraints states:
MySQL requires indexes on foreign keys and referenced keys so that
foreign key checks can be fast and not require a table scan. In the
referencing table, there must be an index where the foreign key
columns are listed as the first columns in the same order. Such an
index is created on the referencing table automatically if it does not
exist. This index might be silently dropped later, if you create
another index that can be used to enforce the foreign key constraint.
index_name, if given, is used as described previously.
Emphasis by me. I don't know what's the issue in your case.
Demo
Explanation of the issue
The behavior mentioned in the question can be reproduced if the table Training is using the MyISAM storage engine. Then creating a foreign key referencing the table Training will produce the mentioned error.
If there's data in the table, then simple dropping of the table would not be the best solution. You can change the storage engine to InnoDB with
ALTER TABLE Training Engine=InnoDB;
Now you can successfully add the foreign key constraint.
I have a table created in MySQL (see the code below). As you see in the code I have a foreign key manager which references the idNo. However I only want to reference to employees with the cat='B'. So I need something like
FOREIGN KEY (manager ) REFERENCES Employee(idNo WHERE cat='B').
Any ideas how I can accomplish this.
idNo SMALLINT AUTO_INCREMENT UNIQUE,
name VARCHAR(25) NOT NULL,
telNo INT(11) NOT NULL,
cat CHAR(1) NOT NULL,
manager SMALLINT,
PRIMARY KEY (idNo ),
FOREIGN KEY (manager ) REFERENCES Employee(idNo) on DELETE CASCADE)ENGINE=INNODB
AUTO_INCREMENT=1000;
Foreign keys do not allow conditions.
If you want to force the condition, you must do it via coding.
It can be done inside your non DB code (Java, PHP, VB,...), or you could create a procedure in MySQL that would be called to perform the insert and that will return an error code if condition is not matched.
If you insert from various codes/application, the procedure is the way to go since it would be centralized.
Create a new UNIQUE KEY in Employee combining idNo and cat.
ALTER TABLE Employee ADD UNIQUE KEY (idNo,cat);
Make a foreign key in the child table that references that new unique key.
ALTER TABLE SomeTable ADD FOREIGN KEY (idNo,cat) REFERENCES Employee(idNo,cat);
Then you just need to make sure cat is constrained to the single value 'B' in the child table. One solution is to create a lookup table containing just the single value 'B'.
CREATE TABLE JustB (cat char(1) PRIMARY KEY);
ALTER TABLE SomeTable ADD FOREIGN KEY(cat) REFERENCES JustB(cat);
Now the only value you can use in the child table is 'B', so naturally it can only reference rows in Employee that have a cat of 'B'.
Another solution would be to use a trigger, but I favor the lookup table.
I'm trying to add a foreign key to my user table.
All of my tables are InnoDB, and are using the same charset. I've got not idea as to why it isn't working :(.
Here is a screenshot of the user table:
As you can see, my userid, is an integer with the max length of 10.
This is the second table (called Content Enabled):
userid, in Content enabler, is identical to the userid in the users table, except it's not a primary index.
When I want to link them via a foreign key, using this query:
ALTER TABLE `contentenabler` ADD FOREIGN KEY ( `userid` ) REFERENCES `tietgen`.`users` (
`userid`
) ON DELETE CASCADE ON UPDATE CASCADE ;
This returns the error Error creating foreign key on userid (check data types)
As far as I can see, the data type are the same, where am I going wrong?
userid is UNSIGNED in your users table, but not your other table.