Is it good to use UNIQUE index? - mysql

I have a register of students, the main table is this one and I just wanna make sure there won't be students with duplicated names, emails, etc. ID is the main key so I don't need to use a PK.
CREATE TABLE student(
id INT UNSIGNED NOT NULL,
campus_id SMALLINT UNSIGNED NOT NULL,
first_career_id SMALLINT UNSIGNED NOT NULL,
second_career_id SMALLINT UNSIGNED NULL,
name VARCHAR(70) NOT NULL,
password CHAR(60) NOT NULL,
email VARCHAR(100) NOT NULL,
phone TINYINT UNSIGNED NULL,
UNIQUE(id),
UNIQUE(name),
UNIQUE(email),
UNIQUE(phone),
FOREIGN KEY(first_career_id)
REFERENCES career(id)
ON DELETE CASCADE,
FOREIGN KEY(second_career_id)
REFERENCES career(id)
ON DELETE CASCADE
) ENGINE=INNODB;
I could validate those fields when inserting them through the applicacion, which way is better?

If uniqueness is a domain constraint (which it is in your example), then create a UNIQUE constraint on the columns(s) (which creates a unique index for you).
ALTER TABLE student
ADD CONSTRAINT ux_student_email
UNIQUE (email)
If you can be 100% sure (and future proof) that no other application will insert data, then ensuring uniqueness in the application is fine. If you value correctness, add a constraint at the database level in addition to checking in the application.

Related

Enforce composite unique constraint that depends on parent column value

With provided schema i want to somehow enforce that there is unique reserved_seat:seat_id per showing. In other words you can't reserve specific seat if it is already reserved in that showing.
One option is to also add showing_id to reservation_seat (which is redundant) and then make unique constraint on (showing_id, seat_id).
Can this be done in sql or it falls to application code?
The DDL:
CREATE TABLE showing
(
id INT NOT NULL AUTO_INCREMENT,
name VARCHAR(45) NOT NULL,
PRIMARY KEY (id)
)
CREATE TABLE reservation
(
id INT NOT NULL AUTO_INCREMENT,
showing_id INT NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY (showing_id) REFERENCES showing(id)
)
CREATE TABLE reservation_seat
(
id INT NOT NULL AUTO_INCREMENT,
reservation_id INT NOT NULL,
seat_id INT NOT NULL,
confirmed TINYINT,
PRIMARY KEY (id),
FOREIGN KEY (reservation_id) REFERENCES reservation(id),
FOREIGN KEY (seat_id) REFERENCES seat(id)
)
CREATE TABLE seat
(
id INT NOT NULL AUTO_INCREMENT,
row VARCHAR(45) NOT NULL,
column VARCHAR(45) NOT NULL,
PRIMARY KEY (id)
)
I believe that this is one of those rare cases where the use of surrogate keys (auto_increment id's) instead of natural keys has led you astray. Consider how your table definitions would look if you used natural keys instead:
CREATE TABLE showing
(
name VARCHAR(45) NOT NULL, -- globally unique
PRIMARY KEY (name)
)
CREATE TABLE reservation
(
showing_name VARCHAR(45) NOT NULL,
name VARCHAR(45) NOT NULL, -- only unique within showing_name
PRIMARY KEY (name, showing_name),
FOREIGN KEY (showing_name) REFERENCES showing(name)
)
CREATE TABLE reservation_seat
(
showing_name VARCHAR(45) NOT NULL,
reservation_name VARCHAR(45) NOT NULL,
seat_row VARCHAR(45) NOT NULL,
seat_column VARCHAR(45) NOT NULL,
confirmed TINYINT,
PRIMARY KEY (showing_name, reservation_name, seat_row, seat_column),
FOREIGN KEY (showing_name, reservation_name) REFERENCES reservation(showing_name, name),
FOREIGN KEY (seat_row, seat_column) REFERENCES seat(row, column)
)
Now you can add your reserved seat per showing constraint as an Alternate Key on reservation_seat:
CREATE TABLE reservation_seat
(
showing_name VARCHAR(45) NOT NULL,
reservation_name VARCHAR(45) NOT NULL,
seat_row VARCHAR(45) NOT NULL,
seat_column VARCHAR(45) NOT NULL,
confirmed TINYINT,
PRIMARY KEY (showing_name, reservation_name, seat_row, seat_column),
FOREIGN KEY (showing_name, reservation_name) REFERENCES reservation(showing_name, name),
FOREIGN KEY (seat_row, seat_column) REFERENCES seat(row, column),
CONSTRAINT UC_seat_showing_reserved UNIQUE(showing_name, seat_row, seat_column)
)
However, this makes it clear that the primary key is superfluous because it's just a weaker version of the constraint that we have added, so we should replace it with our new constraint.
CREATE TABLE reservation_seat
(
showing_name VARCHAR(45) NOT NULL,
reservation_name VARCHAR(45) NOT NULL,
seat_row VARCHAR(45) NOT NULL,
seat_column VARCHAR(45) NOT NULL,
confirmed TINYINT,
PRIMARY KEY (showing_name, seat_row, seat_column),
FOREIGN KEY (showing_name, reservation_name) REFERENCES reservation(showing_name, name),
FOREIGN KEY (seat_row, seat_column) REFERENCES seat(row, column)
)
We may worry now that our reservation_seat could be referencing a reservation with a different showing_id than the reservation_seat itself, but that's not a problem for natural keys because the first foreign key reference prevents that.
Now all we need to do is to translate this back into surrogate keys:
CREATE TABLE reservation_seat
(
id INT NOT NULL AUTO_INCREMENT,
showing_id INT NOT NULL,
reservation_id INT NOT NULL,
seat_id INT NOT NULL,
confirmed TINYINT,
PRIMARY KEY (id),
FOREIGN KEY (showing_id, reservation_id) REFERENCES reservation(showing_id, id),
FOREIGN KEY (seat_id) REFERENCES seat(id),
CONSTRAINT UC_seat_showing_reserved UNIQUE(showing_id, seat_id)
)
Because we're making the reservation_seat(id) the primary key, we have to change the named PK definition back into a unique constraint. Compared to your original reservation_seat definition, we end up with showing_id added, but with the modified stronger first foreign key definition we now insure both that reservation_seat are unique within a showing and that reservation_seat cannot have a showing_id different from its parent reservation.
(Note: you will probably have to quote the 'row' and 'column' column names in the SQL code above)
Additional Note: DBMS's vary on this (and I am not sure about MySql in this case), but many will require that a Foreign Key relation have a corresponding Primary Key or Unique Constraint on the target (referenced) table. This would mean that you would have to alter the reservation table with a new constraint like:
CONSTRAINT UC_showing_reserved UNIQUE(showing_id, id)
to match the new FK definition on reservation_seat that I suggested above:
FOREIGN KEY (showing_id, reservation_id) REFERENCES reservation(showing_id, id),
Technically, this would be a redundant constraint since it is a weaker version of the primary key on the reservation table, but in this case SQL would probably still require it to implement the FK.
Does it take 90 characters to specify a "seat"? The seats I am familiar with are like "103-45" or "J17". Or even "Sec 4 Row 43 Seat 105". You have not mentioned it, but row/column is not adequate to answer the question "are these two seats adjacent?"
My first approach to the problem is to get rid of the table seat, other than being able to enumerate all the seats in a venue.
Then I would question the table reservation_seat, which smells like a many-to-many mapping (plus a flag). Many:many implies non uniqueness. So, something has to give.
The raw, unnormalized, data seems to be
showing: showing_id (PK), date, time, location
reservation: showing_id, seat, confirmed
Having this (on reservation) probably answers your question:
PRIMARY KEY(showing_id, seat)
It ties the two tables together, provides a 'natural' PK, and still allows for the confirmed flag.
I don't know your logic for "confirming". I assume that you cannot reassign a seat while it is waiting to be confirmed?
Back to my starting comment. seat VARCHAR(15) might be appropriate. And, if you need it, another table could have
CREATE TABLE venue (
venue_id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT,
name VARCHAR (144) NOT NULL,
location ...
capacity SMALLINT UNSIGNED NOT NULL,
...
PRIMARY KEY(venue_id)
) ENGINE=InnoDB
CREATE TABLE seat (
venue_id SMALLINT UNSIGNED NOT NULL,
seat_num VARCHAR(30) NOT NULL,
is_handicap ...,
strip_num SMALLINT UNSIGNED NOT NULL, -- see below
PRIMARY KEY(venue_id, seat_num)
) ENGINE=InnoDB
This fails to take care of a venue where you sometimes want to block off the balcony, thereby invalidating some of the seats. Having a different venue and id with most of the info the same might be direction to take.
Be sure to use transactions (BEGIN..COMMIT and FOR UPDATE) where appropriate.
CREATE TABLE showing (
showing_id MEDIUM UNSIGNED NOT NULL AUTO_INCREMENT,
venue_id SMALLINT UNSIGNED NOT NULL,
date ...
notes ...
PRIMARY KEY(showing_id)
) ENGINE=InnoDB
To deal with seat adjacency, I suggest manually assign each stretch of adjacent seats a "strip" number that stops at aisles, posts, etc. Alas, that is not adequate for a middle section where seat "1" is in the middle, with even numbers going one way and odd going the other way. So K-8 and K-9 are quite far apart, but K-8 and K-10 are adjacent, in spite sorting far apart.
As for confirmed, it "belongs" in reservation. But it might be more convenient for other actions to have it in seat. We may need to work out the SQL statements to make that decision. Also, the SQL statements are necessary for deciding on what secondary INDEXes to have.

Assign different muliple foreign key in one table from other table in MySQL

I was planning to make 2 tables for user infomation . The first lager table named userInfo has all data . The second smaller table named loginDetails have the minimum data to log in .
My problem is : I could not assign multiple foreign key .
MySQL said:
#1005 - Can't create table `test`.`logindetails` (errno: 150 "Foreign key constraint is incorrectly formed")
Here is code :
CREATE TABLE userInfo
(
userInfoUserNumber INT(255) UNSIGNED AUTO_INCREMENT UNIQUE NOT NULL,
userInfoUserName VARCHAR(255) UNIQUE NOT NULL,
userInfoPassword VARCHAR(255) NOT NULL,
userInfoFirstName VARCHAR(255) NOT NULL,
userInfoLastName VARCHAR(255) NOT NULL,
userInfoPhoneNumber INT(255) UNSIGNED ZEROFILL UNIQUE NOT NULL,
userInfoPlaceWithoutDivision VARCHAR(255) NOT NULL,
userInfoDivision VARCHAR(255) NOT NULL,
userInfoEmail VARCHAR(255) UNIQUE,
userInfoProfilePicture VARCHAR(255),
PRIMARY KEY (userInfoUserNumber)
);
CREATE TABLE loginDetails
(
loginDetailsUserNumber INT(255) UNSIGNED AUTO_INCREMENT UNIQUE NOT NULL,
loginDetailsUserName VARCHAR(255) UNIQUE NOT NULL,
loginDetailsPassword VARCHAR(255) NOT NULL,
loginDetailsPhoneNumber INT(255) UNSIGNED ZEROFILL UNIQUE NOT NULL,
loginDetailsEmail VARCHAR(255) UNIQUE,
PRIMARY KEY (loginDetailsUserNumber) ,
FOREIGN KEY (loginDetailsUserName) REFERENCES userInfo(userInfoUserName)
ON DELETE SET NULL
ON UPDATE CASCADE,
FOREIGN KEY (loginDetailsPassword) REFERENCES userInfo(userInfoPassword)
ON DELETE SET NULL
ON UPDATE CASCADE,
FOREIGN KEY (loginDetailsPhoneNumber) REFERENCES userInfo(userInfoPhoneNumber)
ON DELETE SET NULL
ON UPDATE CASCADE,
FOREIGN KEY (loginDetailsEmail) REFERENCES userInfo(userInfoEmail)
ON DELETE SET NULL
ON UPDATE CASCADE
);
[ In short : suppose my first table has 10 columns , my second table has 5 columns , i want to choose any 4 columns from 1st table and copy to my second table ]
Question 2 :
why this statement is error ? please explain
INSERT INTO userInfo(userInfoUserName,userInfoPassword,userInfoFirstName,userInfoLastName,userInfoPhoneNumber,userInfoPlaceWithoutDivision,userInfoDivision)
VALUES (cat,SHA1(cat),white,cat,01111111111,myplace,mydivision);
You can declare a foreign key only if the column you reference is the leftmost column of a key.
Traditionally, you'd reference only a unique or primary key, but InnoDB (strangely) allows a foreign key to reference any kind of key or partial key.
Your column userInfo.userInfoPassword is not part of any key.
It's not clear what purpose there could be for declaring all those foreign keys. If you want them to cascade, to always remain the same value in the userInfo table, then why are they stored in both tables at all? Just store them in one table.
i want to create a different table (loginDetails) taking 4 columns from userinfo
Why? You don't have to create a different table if you want to fetch a result set with just those four columns. You just specify the columns you want in a query instead of using SELECT *.
SELECT loginDetailsUserNumber,
loginDetailsUserName,
loginDetailsPassword,
loginDetailsPhoneNumber,
loginDetailsEmail
FROM userInfo;
Another option would be to use CREATE VIEW to define a view with those four columns, and then you could use SELECT * from your view.
CREATE VIEW loginDetails AS
SELECT loginDetailsUserNumber,
loginDetailsUserName,
loginDetailsPassword,
loginDetailsPhoneNumber,
loginDetailsEmail
FROM userInfo;
SELECT * FROM loginDetails;
When designing foreign key relationships, you should be linking using primary keys. This would suggest:
CREATE TABLE loginDetails (
loginDetailsUserNumber INT UNSIGNED AUTO_INCREMENT UNIQUE NOT NULL,
loginDetailsUserInfoUserNumber INT UNSIGNED,
loginDetailsUserName VARCHAR(255) UNIQUE NOT NULL,
loginDetailsPassword VARCHAR(255) NOT NULL,
loginDetailsPhoneNumber INT(255) UNSIGNED ZEROFILL UNIQUE NOT NULL,
loginDetailsEmail VARCHAR(255) UNIQUE,
PRIMARY KEY (loginDetailsUserNumber) ,
FOREIGN KEY (loginDetailsUserInfoUserNumber) REFERENCES userInfo(userInfoUserNumber)
ON DELETE SET NULL
ON UPDATE CASCADE,
);
In other words, you can keep the duplicated columns (perhaps a user changes his/her name or password and you want the version associated with the login). BUT, you should be assigning a user number at login and putting that id in the table.

Usage of MySQL foreign key referencing multiple columns

I just stumbled across possibility of MySQL foreign key to reference multiple columns. I would like to know what is main purpose of multi-column foreign keys like shown bellow
ALTER TABLE `device`
ADD CONSTRAINT `fk_device_user`
FOREIGN KEY (`user_created_id` , `user_updated_id` , `user_deleted_id`)
REFERENCES `user` (`id` , `id` , `id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION;
My questions are
Is it the same as creating three independent foreign keys?
Are there any pros / cons of using one or another?
What is the exact use-case for this? (main question)
Is it the same as creating three independent foreign keys?
No. Consider the following.
First off, it is not useful to think of it as (id,id,id), but rather (id1,id2,id3) in reality. Because a tuple of (id,id,id) would have no value over just a single column index on id. As such you will see the schema below that depicts that.
create schema FKtest001;
use FKtest001;
create table user
( id int auto_increment primary key,
fullname varchar(100) not null,
id1 int not null,
id2 int not null,
id3 int not null,
index `idkUserTuple` (id1,id2,id3)
);
create table device
( id int auto_increment primary key,
something varchar(100) not null,
user_created_id int not null,
user_updated_id int not null,
user_deleted_id int not null,
foreign key `fk_device_user` (`user_created_id` , `user_updated_id` , `user_deleted_id`)
REFERENCES `user` (`id1` , `id2` , `id3`)
);
show create table device;
CREATE TABLE `device` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`something` varchar(100) NOT NULL,
`user_created_id` int(11) NOT NULL,
`user_updated_id` int(11) NOT NULL,
`user_deleted_id` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `fk_device_user` (`user_created_id`,`user_updated_id`,`user_deleted_id`),
CONSTRAINT `device_ibfk_1` FOREIGN KEY (`user_created_id`, `user_updated_id`, `user_deleted_id`) REFERENCES `user` (`id1`, `id2`, `id3`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
show indexes from device; -- shows 2 indexes (a PK, and composite BTREE)
-- FOCUS heavily on the `Seq_in_index` column for the above
-- xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
drop table device;
drop table user;
create table user
( id int auto_increment primary key,
fullname varchar(100) not null,
id1 int not null,
id2 int not null,
id3 int not null,
index `idkUser1` (id1),
index `idkUser2` (id2),
index `idkUser3` (id3)
);
create table device
( id int auto_increment primary key,
something varchar(100) not null,
user_created_id int not null,
user_updated_id int not null,
user_deleted_id int not null,
foreign key `fk_device_user1` (`user_created_id`)
REFERENCES `user` (`id1`),
foreign key `fk_device_user2` (`user_updated_id`)
REFERENCES `user` (`id2`),
foreign key `fk_device_user3` (`user_deleted_id`)
REFERENCES `user` (`id3`)
);
show create table device;
CREATE TABLE `device` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`something` varchar(100) NOT NULL,
`user_created_id` int(11) NOT NULL,
`user_updated_id` int(11) NOT NULL,
`user_deleted_id` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `fk_device_user1` (`user_created_id`),
KEY `fk_device_user2` (`user_updated_id`),
KEY `fk_device_user3` (`user_deleted_id`),
CONSTRAINT `device_ibfk_1` FOREIGN KEY (`user_created_id`) REFERENCES `user` (`id1`),
CONSTRAINT `device_ibfk_2` FOREIGN KEY (`user_updated_id`) REFERENCES `user` (`id2`),
CONSTRAINT `device_ibfk_3` FOREIGN KEY (`user_deleted_id`) REFERENCES `user` (`id3`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
show indexes from device; -- shows 4 indexes (a PK, and 3 indiv FK indexes)
-- FOCUS heavily on the `Seq_in_index` column for the above
There are 2 sections there. The show indexes from device will show the difference of, in the top part, 2 indexes maintained. In the bottom part, 4 indexes maintained. If for some reason the index tuple in the top part is useful for the system, then that tuple approach is certainly the way to go.
The reason is the following. The tuple exists as a group. Think of it as an instance of a set that has meaning as a group. Compare that to the mere existence of the individual parts, and there is a difference. It is not that the users exist, it is that there is a user row that has that tuple as an existence.
Are there any pros / cons of using one or another?
The pros were described above in the last paragraph: existence as an actual grouping in the user table as a tuple.
They are apple and oranges and used for different purposes.
What is the exact use-case for this? (main question)
A use case would be something that requires the existence of the tuple as a group, as opposed to the existence of the individual items. It is used for what is called compositing. Compositing FK's in particular. See this answer of mine Here as one case.
In short, it is when you want to enforce special hard to think of solutions that require Referential Integrity (RI) at a composited level (groupings) of other entities. Many people think it can't be done so they first think TRIGGER enforcement or front-end Enforcement. Fortunately those use cases can be achieved via the FK Composites thus leaving RI at the db level where it should be (and never at the front-end).
Addendum
Request from OP for a better real life example than the link above.
Consider the following schema:
CREATE SCHEMA testRealLifeTuple;
USE testRealLifeTuple;
CREATE TABLE contacts
( id INT AUTO_INCREMENT PRIMARY KEY,
fullname VARCHAR(100) NOT NULL
-- etc
);
CREATE TABLE tupleHolder
( -- a tuple representing a necessary Three-some validation
-- and vetting to get financing
--
-- If you can't vett these 3, you can't have my supercomputer financed
--
id INT AUTO_INCREMENT PRIMARY KEY,
CEO INT NOT NULL, -- Chief Executive Officer
CFO INT NOT NULL, -- Chief Financial Officer
CIO INT NOT NULL, -- Chief Geek
creditWorthiness INT NOT NULL, -- 1 to 100. 100 is best
-- the unique index is necessary for the device FK to succeed
UNIQUE INDEX `idk_ContactTuple` (CEO,CFO,CIO), -- No duplicates ever. Good for re-use
FOREIGN KEY `fk_th_ceo` (`CEO`) REFERENCES `contacts` (`id`),
FOREIGN KEY `fk_th_cfo` (`CFO`) REFERENCES `contacts` (`id`),
FOREIGN KEY `fk_th_cio` (`CIO`) REFERENCES `contacts` (`id`)
);
CREATE TABLE device
( -- An Expensive Device, typically our Supercomputer that requires Financing.
-- This device is so wildly expense we want to limit data changes
--
-- Note that the GRANTS (privileges) on this table are restricted.
--
id INT AUTO_INCREMENT PRIMARY KEY,
something VARCHAR(100) NOT NULL,
CEO INT NOT NULL, -- Chief Executive Officer
CFO INT NOT NULL, -- Chief Financial Officer
CIO INT NOT NULL, -- Chief Geek
FOREIGN KEY `fk_device_2_tuple` (`CEO` , `CFO` , `CIO`)
REFERENCES `tupleHolder` (`CEO` , `CFO` , `CIO`)
--
-- Note that the GRANTS (privileges) on this table are restricted.
--
);
DROP SCHEMA testRealLifeTuple;
The highlights of this schema come down to the UNIQUE KEY in tupleHolder table, the FK in device, the GRANT restriction (grants not shown), and the fact that the device is shielded from tomfoolery edits in the tupleHolder because of, as mentioned:
GRANTS
That the FK must be honored, so the tupleHolder can't be messed with
If the tupleHolder was messed with (the 3 contacts ids), then the FK would be violated.
Said another way, it is NO WAY the same as the device having an FK based on a single column in device, call it [device.badIdea INT], that would FK back to tupleHolder.id.
Also, as mentioned earlier, this differs from merely having the contacts exist. Rather, it matters that the composition of contacts exists, it is a tuple. And in our case the tuple has been vetted, and has a credit worthiness rating, and the id's in that tuple can't be messed with, after a device is bought, unless sufficient GRANTS allow it. And even then, the FK is in place.
It may take 15 minutes for that to sink in, but there is a Huge difference.
I hope this helps.

#1005 - mysql - can't create a foreign key

I created this table here:
CREATE TABLE izpulnitel(
id INT(6) UNSIGNED AUTO_INCREMENT PRIMARY KEY,
ime VARCHAR(30) NOT NULL,
familia VARCHAR(30) NOT NULL,
img BLOB
);
and this other table:
CREATE TABLE album(
id INT(6) UNSIGNED AUTO_INCREMENT PRIMARY KEY,
godina INT(6) NULL,
opisanie TEXT
);
and I want to creat a third table with 2 foreign keys:
CREATE TABLE pesen (
id INT(6) UNSIGNED AUTO_INCREMENT PRIMARY KEY,
ime VARCHAR(30) NOT NULL,
tekst TEXT,
izpulnitel_id INT(6),
album_id INT(6),
INDEX par_ind (izpulnitel_id),
INDEX par_indx (album_id),
FOREIGN KEY (izpulnitel_id)
REFERENCES izpulnitel(id)
ON DELETE CASCADE,
FOREIGN KEY (album_id)
REFERENCES album(id)
ON DELETE CASCADE
)
But for some reason the table can't be created. There is some kind of error around the foreign key operators, most likely it's the INDEX tag.
I tried adding seperate queries with CREATE INDEX ... but it doesn't seem to work. Should I have created these indexes with the created of the parent tables or there is another solution? What are these INDEX-s made for anyway ?
When creating foreign keys it is extremely important(read: required) that the columns being used are of the same types.
From the MySQL documentation:
Corresponding columns in the foreign key and the referenced key must have similar data types. The size and sign of integer types must be the same. The length of string types need not be the same. For nonbinary (character) string columns, the character set and collation must be the same.
Your id columns in the first two tables are INT UNSIGNED, but the izpulnitel_id and album_id in your 3rd table are INT(i.e signed). Signedness is one of the things that must match. Change those columns to INT(6) UNSIGNED and you should be on your way.
The indexes are there for performance reasons. Again from the MySQL documentation:
MySQL requires indexes on foreign keys and referenced keys so that foreign key checks can be fast and not require a table scan.
CREATE TABLE pesen ( id INT(6) UNSIGNED AUTO_INCREMENT PRIMARY KEY, ime VARCHAR(30) NOT NULL, tekst TEXT, izpulnitel_id INT(6) UNSIGNED, album_id INT(6) UNSIGNED, INDEX par_ind (izpulnitel_id), INDEX par_indx (album_id), FOREIGN KEY (izpulnitel_id) REFERENCES izpulnitel(id) ON DELETE CASCADE, FOREIGN KEY (album_id) REFERENCES album(id) ON DELETE CASCADE )

Error during the creation of table due to foreign key

I have table1 already in my db.
Table1:
CREATE TABLE `product` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`typename` varchar(255) DEFAULT NULL,
`typecode` varchar(55) DEFAULT NULL,
`parent1` int(11) DEFAULT NULL,
`parent2` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `parent1` (`parent1`),
KEY `parent2` (`parent2`)
) ENGINE=InnoDB AUTO_INCREMENT=396 DEFAULT CHARSET=latin1;
I tried to create the second table with foreign key which has reference to product.typename
this is the creation query I have used.
CREATE TABLE measurements (
id int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
age_group varchar(20) NOT NULL,
article_type varchar(255) DEFAULT NULL,
dimension text ,
createdOn int(11) NOT NULL,
updatedOn int(11) NOT NULL,
createdBy text NOT NULL,
foreign KEY(article_type) references product(typename)
)ENGINE=InnoDB AUTO_INCREMENT=396 DEFAULT CHARSET=latin1;
But this table creation is a failure with the following error.
ERROR 1215 (HY000): Cannot add foreign key constraint
I have done show engine innodb\g
------------------------
LATEST FOREIGN KEY ERROR
------------------------
2015-05-15 19:03:28 131f71000 Error in foreign key constraint of table db/measurements:
foreign KEY(article_type) references product(typename)
)ENGINE=InnoDB AUTO_INCREMENT=396 DEFAULT CHARSET=latin1:
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.6/en/innodb-foreign-key-constraints.html
for correct foreign key definition.
Can some one point me the problem and what is this first columns concept?
Referenced column should be Primary key. Here
foreign KEY(article_type) references product(typename)
you want to reference with typename column which is not PK.
To do it in properly way you should create table ProductType like this:
CREATE TABLE `ProductType` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`typename` varchar(255) DEFAULT NULL,
`typecode` varchar(55) DEFAULT NULL,
) ENGINE=InnoDB AUTO_INCREMENT=396 DEFAULT CHARSET=latin1;
then you can create reference like this:
CREATE TABLE measurements (
id int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
age_group varchar(20) NOT NULL,
IdProductType NOT NULL,
dimension text ,
createdOn int(11) NOT NULL,
updatedOn int(11) NOT NULL,
createdBy text NOT NULL,
foreign KEY(IdProductType) references ProductType(Id)
)ENGINE=InnoDB AUTO_INCREMENT=396 DEFAULT CHARSET=latin1;
Don't forget to do it with Product table.
Above solution is only suggestion, you have to consider your table structure yourself.
A foreign key references a key. This is usually the primary key, but doesn't have to be. In your case however you reference a column (typename) which is not defined as a key. This shows a design flaw.
You decided to use technical IDs as primary keys for your tables. You can do this. But if you do this, keep two things in mind:
You've created IDs in order to link tables easily. So don't reference a record by another column (such as typename), but by its ID.
You must still make sure that the table's natural key is unique.
As to point 2: What is your table's natural key? What is or are the fields that uniquely identify a record (apart from your technically created ID)? Is it typename? Is typename the product's name and must it be unique? Or is this typecode? Whatever it is, give this field a unique constraint, so you cannot have the same product twice in your table.
Maybe it would help you learn to design your database, if you didn't use technical IDs at all. Give it a thought.
Just a side note: Be aware that MySQL has a strange way of using the keyword KEY:
create table t (col int key);
Here KEY really means the table's primary key. col cannot be null and col must be unique. It is short for:
create table t (col int primary key);
However,
create table t (col int, key(col));
is something entirely else. Here, KEY is not short for PRIMARY KEY, but a synonym for INDEX. col can be null, col doesn't have to be unique. So better use the synonym INDEX to make it clear to a reader:
create table t (col int, index(col));
When working with an additional ID, as you are doing, you even need a unique index:
create table t (id int primary key, col int, unique index(col));
or
create table t (id int, col int, primary key(id), unique index(col));