MySQL: is primary key unique by default? - mysql

If I define a column as a primary key in MySQL, is it also unique key by default or do I need to also define it as unique key (in case I want it to be unique)?
I saw this question What is the difference b/w Primary Key and Unique Key that explain the difference between the two, but doesn't exactly answer my question.
Does PK is UK by default or I need to explicitly define it?

Primary key is always unique in every SQL. You dont have to explicitly define it as UNIQUE.
On a side note: You can only have onePrimary key in a table and it never allows null values. Also you can have only one primary key constraint in the table(as the point of creating a primary key is to uniquely identify the row in your table) but you can more than one unique key constraint in your table.
Example:
An employee details table having EmpID as Primary key and EmpPhoneNo as unique key.

Primary key is always unique by definition. Not only in MySQL.
So you don't need any additional unique key.

Note that composite keys may lead to confusion : indeed a primary key can be a composite key, and DESCRIBE will show all of the composite key components as primary keys :
> DESCRIBE foobar;
+----------------------+------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------------------+------------------+------+-----+---------+-------+
| column_A | int(10) unsigned | NO | PRI | NULL | |
| column_B | int(10) unsigned | NO | PRI | NULL | |
+----------------------+------------------+------+-----+---------+-------+
However SHOW CREATE TABLE will show the reality :
> SHOW CREATE TABLE foobar;
+--------+---------------------------…+
| Table | Create Table …|
+--------+---------------------------…+
| foobar | CREATE TABLE `foobar` (
`column_A` int(10) unsigned NOT NULL,
`column_B` int(10) unsigned NOT NULL,
PRIMARY KEY (`column_A`,`column_B`),
KEY `column_B` (`column_B`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 |
+--------+---------------------------…+

Related

MySQL add primary key after foreign key insert

I have two tables (A, B) with a foreign key from B to A.
So A is my parent table and B my child.
I now insert a row in B before the parent row exists in A. So I set the foreign key to an id I know parent A will have, but which is not existing right know. To achieve that I use the option 'SET foreign_key_checks = 0', which allows to set a foreign key in the child B without the existens of the key in the parent A.
My question is, what will happen, if I add the row in A with the missing primary key. Will the foreign key <-> primary key connection work and will it be as fast as normal? Or do I have to drop the fk key and rebuild it?
I use InnoDB and MySQL 5.5.
... and I know that is probably very bad practice...
Or short:
I have a parent and a child table, linked by a foreign key. I add the child first, what happens if I add the parent later?
My question is, what will happen, if I add the row in A with the
missing primary key. Will the foreign key <-> primary key connection
work and will it be as fast as normal? Or do I have to drop the fk key
and rebuild it?
If you will add missing record into parent table, the FK constraint will work as it should be. You will actually solve the data inconsistency.
There is no need to recreate FK.
I tried it myself by creating an example.
CREATE TABLE `parent` (
`idparent` int(11) NOT NULL,
PRIMARY KEY (`idparent`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE `parent` (
`idparent` int(11) NOT NULL,
PRIMARY KEY (`idparent`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
SET foreign_key_checks = 0;
INSERT INTO child (idchild, parentid) VALUES (1,1),(2,2),(3,3),(4,4),(5,5);
SET foreign_key_checks = 1;
INSERT INTO parent (idparent) VALUES (1),(2),(3),(4),(5);
Next, I used explain to get an idea, if the index is used:
EXPLAIN SELECT * from parent p
join child c on c.parentid = p.idparent;
+----+-------------+-------+-------+-----------------+-----------------+---------+----------------------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+-----------------+-----------------+---------+----------------------+------+--------------------------+
| 1 | SIMPLE | p | index | PRIMARY | PRIMARY | 4 | NULL | 5 | Using index |
| 1 | SIMPLE | c | ref | fk_parentid_idx | fk_parentid_idx | 5 | remove_me.p.idparent | 1 | Using where; Using index |
+----+-------------+-------+-------+-----------------+-----------------+---------+----------------------+------+--------------------------+
So it looks like it uses the index, altough at first the foreign key was not set. Therefore, it should be at least speedwise no problem.
INSERT INTO `area` (
`area_id` PRIMARY KEY,
`area_name`,
`chemist_id`FOREIGN KEY
)
VALUES (
[value-1],
[value-2],
[value-3]
)

Referencing table with generated primary keys

I am trying to increase the constraint of my MySQL database schema adding foreign key constraint to each table.
Table 1: USERS
+---------+----------+-------------
| id | username | Other fields
+---------+----------+-------------
| 1 | John |
| 2 | Mark |
+---------+----------+-------------
id INT(11) UNSIGNED NOT NULL AUTO_INCREMENT
username` VARCHAR(50) NOT NULL
PRIMARY KEY (id)
Table 2: DISKS (This has a one to many relationship with USERS)
+---------+----------+-----------+-------------
| id | id_user | disk_name | Other fields
+---------+----------+-----------+-------------
| 1 | 1 | disk A |
| 2 | 2 | disk B |
+---------+----------+-----------+-------------
id INT(11) UNSIGNED NOT NULL AUTO_INCREMENT
id_user INT(11) NOT NULL,
PRIMARY KEY (id,id_user)
INDEX fk_disks_idx (id ASC)
CONSTRAINT fk_disks
FOREIGN KEY (id)
REFERENCES database.USERS (id)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
Table 3: FILES (This has a one to many relationship with DISKS)
+---------+----------+----------+-----------+-------------
| id | id_disk | id_user | file_name | Other fields
+---------+----------+----------+-----------+-------------
| 1 | 1 | 1 | |
| 2 | 2 | 2 | |
+---------+----------+----------+-----------+-------------
id INT(11) UNSIGNED NOT NULL AUTO_INCREMENT
id_user INT(11) NOT NULL
id_disk INT(11) NOT NULL
PRIMARY KEY (id,id_disk,id_user )
INDEX fk_files_idx (id ASC, id_user ASC)
CONSTRAINT fk_files
FOREIGN KEY (id_disk, id_user, id_user)
REFERENCES database.DISKS (id)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
Table 2: FILES_ON_NAS (This has a one to one relationship with FILES)
+-------+----------+----------+----------+-----------+-------------
| id | id_files | id_user | id_disk | file_name | Other fields
+-------+----------+----------+----------+-----------+-------------
| 1 | 1 | 1 | 1 | |
| 2 | 1 | 2 | 2 | |
+-------+----------+----------+----------+-----------+-------------
id INT(11) UNSIGNED NOT NULL AUTO_INCREMENT
id_files INT(11) NOT NULL,
id_user INT(11) NOT NULL,
id_disk INT(11) NOT NULL,
PRIMARY KEY (id,id_files,id_user,id_disk )
INDEX fk_files_on_nas_idx (id ASC)
CONSTRAINT fk_files_on_nas
FOREIGN KEY (id_files,id_user,id_disk)
REFERENCES database.FILES (id,id_user, id_disk)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
Question:
As you can see the more I reference table in cascade the more primary keys I get. How can I design the database to avoid the replication of primary keys and therefore data duplication as well? Should I delete the auto incremented key for each table? Is it a good practice?
Thanks
The ID of the disk is sufficient to uniquely identify a disk. So there's no reason to include the ID of the user into the disk's primary key. It would even be an extremely bad idea, because that means that if a disk's user changes, you would need to modify the primary key.
Same for a file. A file ID uniquely identifies a file. So there's no reason to add the disk ID into the primary key of a file.
I would strongly advise against deleting the auto-incremented keys.
However you don't need to make a new primary key everytime :
If you want that multiple users share one disk, just put a foreign key of id_disk in USERS
If you want that one user can have multiple disks, then put a foreign key of id_user in DISKS instead.
Only use primary keys like that when you face a Many-to-Many relationship. In this case, you need to create a new table to join both tables; with the primary keys of both tables as foreign keys making a coupled primary key like you did.
You might want to read a little about Database Normalization. In your case, i would make the surrogate key id the only primary key in the tables. Something like:
create table users (
id integer not null auto_increment,
username varchar(50),
...,
primary key (id)
);
create table disks (
id integer not null auto_increment,
user_id integer,
diskname varchar(50),
....,
primary key (id),
foreign key (user_id) references users (id)
);
For files you are going to have to answer the question: does file ownership depend on the file directly, or transitively on the disk ownership, or are the ownerships independent? A file owned by John on a disk owned by Jack? Seems ok to me, but your domain might have different rules. In that case, drop the user_id from the files table (otherwise your database won't be in Third normal form).
create table files (
id integer not null auto_increment,
disk_id integer,
user_id integer, -- you have to decide whether this is necessary
filename varchar(50),
....,
primary key (id),
foreign key (disk_id) references disks (id),
foreign key (user_id) references users (id)
);

Can the primary key referenced by a foreign key be a varchar(255) in mysql?

I've run this query on mysql:
ALTER TABLE `connections`
ADD CONSTRAINT `connections_serial_fk`
FOREIGN KEY (`serial`)
REFERENCES `devices`(serial)
ON DELETE CASCADE;
And I'm getting error number 150. I already looked for the documentation and the thing I think that may be causing this error is that the column 'serial' is a varchar(255).
This reference says "innodb permits a foreign key to reference any index column or group of columns".
Does MySql have this restriction?
From my schema:
devices:
| Field | Type | Null | Key | Default | Extra |
| serial | varchar(255) | YES | | NULL | |
connections:
| Field | Type | Null | Key | Default | Extra |
| serial | varchar(255) | YES | | NULL | |
A dbms that conforms to standard SQL will prevent you from setting a foreign key reference to a column (or columns) that doesn't have some kind of unique constraint on it--either PRIMARY KEY or UNIQUE. But MySQL does permit this kind of nonsense under certain circumstances.
Additionally, MySQL requires that the referenced columns be indexed
for performance reasons. However, the system does not enforce a
requirement that the referenced columns be UNIQUE or be declared NOT
NULL. The handling of foreign key references to nonunique 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 UNIQUE (including PRIMARY) and NOT NULL keys.
Foreign keys should reference only a column (or columns) that have been declared either PRIMARY KEY or NOT NULL UNIQUE. If you think you need a foreign key reference to a nonunique column or columns, that's a big red flag, waving in your face, held by a guy yelling, "You have a design problem!"

Mysql errno 150 trying to create table with foreign key references

I'm trying to create a table in mysql with a foreign key reference, like this:
In database A:
CREATE TABLE replication (
id varchar(255) NOT NULL PRIMARY KEY,
uid varchar(255) NOT NULL,
value int(11) NOT NULL,
FOREIGN KEY (uid) REFERENCES databaseB.users(username)
);
In database B i have a table named users like this:
+-----------------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-----------------+--------------+------+-----+---------+-------+
| id | varchar(255) | NO | | NULL | |
| username | varchar(255) | NO | PRI | NULL | |
+-----------------+--------------+------+-----+---------+-------+
When i try to create table replication, gives me the following error:
ERROR 1005 (HY000): Can't create table 'databaseA.replication' (errno: 150)
Any idea? Thanks in advance!
You need to add an index to uid or you will not be able to set it up to reference anything.
Also, typically in your DatabaseB you would have your users table have a Primary Key on ID and a unique index on username. Then you would set up a foreign key from DatabaseA.replication REFERENCES databaseB.users(id)
you cant make reference key with two different types
in databaseA is integer uid and databaseB is varchar
try this
FOREIGN KEY (uid) REFERENCES databaseB.users(id)
Are both databases of the same type (MyISAM, INNODB)? You cannot point a foreign key to a MyISAM DB.
This error can also occur when you are mixing types for the field type (which is the same in this case, varchar(255)), or when the encodings of the fields are different.
Another reason could be that you don't have access to the other database.
Thetype of the field in a foreign key must be the same as the type of the column they're referencing.I think this problem error because type of the field in a foreign key is different from the column they're referencing.
Your foreign key field is uid and referencing table your field is username.Here is the problem.Try to use same type of field in both table like
FOREIGN KEY (uid) REFERENCES databaseB.users(id)
Hope you understand and works for you.

Multiple Primary/Foreign Keys

I'm going to be honest, I'm a bit hazy on how primary and foreign keys work. I was told to setup my database by someone on here so that I had 7 tables, one for organizations, one for categories, services, and cultures, and three cross reference tables between organization on one hand, and categories, services and cultures on the other.
Take the org_culture_xref table, for instance. I have two columns, one is org_id, which is the same as the org_id column (primary key) of the organization table. The other is cult_id, which is the same as the cult_id column (primary key) of the culture table.
I believe both of the columns of the org_culture_xref table are primary and foreign keys. However, this doesn't seem to allow me to have multiple values for both of these columns. I want to be able to have several relationships between organizations and cultures - as in every organization can be associated with multiple cultures and every culture can be associated with multiple organizations.
How do I ensure that I can have multiple values for both columns?
What you're talking about is a Many-To-Many relationship. You're on the right path using a cross-reference table.
It's good to review how foreign and primary keys work; there can only be one primary key per table, but there can be multiple foreign keys. However, note that a primary key doesn't have to be limited to one column; you can have a primary key that spans two, three, or more columns.
Your solution here is to have two foreign key, one for each column/table relationship, and one primary key that spans across both tables.
Here's an example of a table I used at one time, which links cities and counties in a many-to-many relationship.
mysql> show create table xref_cities_counties\G
*************************** 1. row ***************************
Table: xref_cities_counties
Create Table: CREATE TABLE `xref_cities_counties` (
`city_id` int(10) unsigned NOT NULL,
`county_id` tinyint(3) unsigned NOT NULL,
PRIMARY KEY (`city_id`,`county_id`),
KEY `city_id` (`city_id`),
KEY `county_id` (`county_id`),
CONSTRAINT `fk_xrefcitiescounties_cityid` FOREIGN KEY (`city_id`) REFERENCES `florida_cities` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `fk_xrefcitiescounties_countyid` FOREIGN KEY (`county_id`) REFERENCES `florida_counties` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=latin1
1 row in set (0.00 sec)
mysql>
mysql>
mysql> describe xref_cities_counties;
+-----------+---------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-----------+---------------------+------+-----+---------+-------+
| city_id | int(10) unsigned | NO | PRI | | |
| county_id | tinyint(3) unsigned | NO | PRI | | |
+-----------+---------------------+------+-----+---------+-------+
2 rows in set (0.00 sec)
mysql>
I'd suggest some extra reading on the topic. It seems like you're on a good start.
The primary key for your cross ref table will be both of the columns, meaning that the combination of two ids must be unique but you can have repeats of individiual ids in either column.
Here is a statement to create such a table:
CREATE TABLE `x` (
`a_id` varchar(6) NOT NULL default '',
`b_id` varchar(6) NOT NULL default '',
PRIMARY KEY (`a_id`,`b_id`)
)
The issue itself and the cross ref table approach to many-to-many relationships applies to most (if not all) relational databases, but since I haven't used mysql in around 10 years I got that code from here, which seems to have a detailed discussion of the topic with mysql specific code.
Each column of org_culture_xref is a foreign key: org_id is a foreign key to organization, and cult_id is a foreign key to culture. The two of them together are the primary key of org_culture_xref.
So, something along these lines:
CREATE TABLE org_culture_xref
(
org_id NUMERIC(10) NOT NULL,
cult_id NUMERIC(10) NOT NULL,
PRIMARY KEY (org_id, cult_id),
FOREIGN KEY (org_id) REFERENCES organization (org_id),
FOREIGN KEY (cult_id) REFERENCES culture (cult_id)
);