Primary and foreign key in SQL? - mysql

I am not sure about this , but do I need to create foreign key explicitly in the SQL command?
This guy did this:
CREATE TABLE languages (
lang_id TINYINT UNSIGNED NOT NULL AUTO_INCREMENT,
lang VARCHAR(60) NOT NULL,
lang_eng VARCHAR(20) NOT NULL,
PRIMARY KEY (lang_id),
UNIQUE (lang)
);
CREATE TABLE threads (
thread_id INT UNSIGNED NOT NULL AUTO_INCREMENT,
lang_id TINYINT(3) UNSIGNED NOT NULL,
user_id INT UNSIGNED NOT NULL,
subject VARCHAR(150) NOT NULL,
PRIMARY KEY (thread_id),
INDEX (lang_id),
INDEX (user_id)
);
In this case, does it mean that INDEX(lang_id) becomes FOREIGN KEY automatically? I know INDEX makes search go faster, but I don't understand the part about foreign key
I would really appreciate any answer

No. An index is just that... an index on a field. A foreign key tells MySQL that "this particular field MUST have a matching record in that table over there".
MySQL's internal design requires that all fields used as foreign keys be indexed, but modern versions will automatically create that index for you.
The converse is not true, whoever. Adding an index to a field does not turn it into a foreign key - a foreign key definition must also include what the foreign table/field is, and a simple index declaration has none of that information.
For your sample table, you'd need to have
...
INDEX (lang_id),
FOREIGN KEY (lang_id) REFERENCES languages (lang_id),
...
to produce a foreign key.

A foreign key means that the value(s) must exist in the referenced column(s). It is not automatic - you need to write it explicitly.
FOREIGN KEY lang_id REFERENCES languages (lang_id)

No a foreign key has to be explicitly declared
CREATE TABLE threads (
thread_id INT UNSIGNED NOT NULL AUTO_INCREMENT,
lang_id TINYINT(3) UNSIGNED NOT NULL FOREIGN KEY FK_1 REFERENCES languages(lang_id),
user_id INT UNSIGNED NOT NULL,
subject VARCHAR(150) NOT NULL,
PRIMARY KEY (thread_id),
INDEX (lang_id),
INDEX (user_id)
);

What you now have are two tables with primary keys and indexes on those primary key values.
You could stop here if you want but you won't have declared referential integrity enforcing that relationship between laguage and threads tables.
To do that you would explicitly create a foreign key relationship as explained here - http://dev.mysql.com/doc/refman/5.5/en/innodb-foreign-key-constraints.html

The differences are as follows :-
The primary key identifies a record uniquely in a table with multiple rows.
An index is a generic term, where by you can create more than one index for a table, in this case the database creates indexes based on the columns that you specified, so that when you query the appropriate index will kick in and give you results faster.
A foreign key on the other hand says that this column in table b, is the primary column in table A, so that whenever you enter rows into table B the databse will check that the specified column/data exists in table A otherwise it will throw an error.

Related

mysql foreign key using primary key

I have two tables,
diary with columns
id primary key
Narrative text
and
master with columns
id primary key
Diaryid int
EventDate date
Location int
I want to ensure that master(Diaryid) is always a valid diary(id)
Can I use foreign key to achieve this? Bearing in mind that one key is a primary key and the other int.
Any advice would be apprecaited.
Yes you cqan achieve this by using a FOREIGN KEY.
Also you should define it as NOT NULL, so that only exiasting diary keys are allowed
CREATE TABLE master(Diaryid BIGINT NOT NULL
, FOREIGN KEY (Diaryid)
REFERENCES diary(id)
);
You cqan add
ON DELETE CASCADE
So that the Master row will be deleted also when the diary rows gets removed
Don't know what is different but problem is solved.
CREATE TABLE diary(id int AUTO_INCREMENT, Narrative text, PRIMARY KEY(id))
CREATE TABLE master(id int AUTO_INCREMENT, Diaryid int,datemade date, FOREIGN KEY(Diaryid) REFERENCES diary(id), primary key (id))
So am now adding diary records and can have multiple master records so long as the Diaryid is valid
Thanks

Error on creating foreign key relationship between non-primary key

I have two table
CREATE TABLE `abc` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`ref_id` varchar(20) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `ref_id_UNIQUE` (`ref_id`)
)
CREATE TABLE `xyz` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`ref_id` varchar(20) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `ref_id_UNIQUE` (`ref_id`)
)
I want to make foreign key relation ship between xyz's ref_id and abc's ref_id .But Mysql gives error 1215.
You should make the foreign key relationships to the primary keys. I know that MySQL allows foreign key relationships to anything with an index. But the correct practice is to use primary keys.
So declare the table like this:
CREATE TABLE `xyz` (
`id` int(11) NOT NULL AUTO_INCREMENT,
abc_id int DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `ref_id_UNIQUE` (`abc_id`),
ADD CONSTRAINT fk_xyz_abc FOREIGN KEY (abc_id) REFERENCES abc(id)
);
If you want the ref_id for an xyz row, then use JOIN to get the information.
Take a look at Gordon Linoff's answer, his suggestion makes sense, even though it does not answer the question. So what can cause an error when you intend to create a foreign key relationship? The obvious possibility is syntax error and typo, so you will need to check against those and fix any such problems.
Another possibility is that you have inconsistency, that is, you try to create a foreign key constraint in one of your tables, but not all the values have exact matches. So, assuming that you have Foo and Bar table and you intend Foo.lorem to be a foreign key referencing Bar.ipsum, then you will need to ensure that all the values you have for Foo.lorem has a Bar.ipsum pair with the exact same values (except null). If that's not true, then your foreign key constraint will not be successfully created. Find such inconsistencies:
select distinct Foo.lorem
from Foo
where not (Foo.lorem is null) and
not exists (select 1 from Bar where Foo.lorem = Bar.ipsum);
Read the lines carefully and make sure you fix any such Foo.lorem values.

#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 )

Mysql too slow on simple query between two tables

Good morning,
I've two tables, ANALISI with 1462632 records and PAZIENTE with 1408146 records, this simple count using one of the index of PAZIENTE require about 30 seconds to give back about 65000 records
SELECT COUNT(analisi0_.ID_ANALISI) AS col_0_0_
FROM Analisi analisi0_
INNER JOIN Paziente paziente1_ ON analisi0_.ID_PAZIENTE = paziente1_.ID_PAZIENTE
WHERE (paziente1_.nome LIKE 'MARIA%')
I've also tried adding an index on analisi0_.ID_PAZIENTE but with no good results.
Is there a way to enhance performance?
This is the corrisponding explain that seems ok to me
CREATE TABLE ANALISI
(
ID_ANALISI INT UNSIGNED NOT NULL AUTO_INCREMENT,
ID_PAZIENTE INT UNSIGNED NOT NULL,
ID_SESSIONE INT UNSIGNED NOT NULL,
TRACCIATO TINYINT UNSIGNED NOT NULL,
CAMPIONE VARCHAR(30),
ID_PATOLOGICO TINYINT UNSIGNED,
REPARTO VARCHAR(40),
TOTALE_PROTEINE FLOAT,
RAPP_AG FLOAT,
ID_ANALISI_LINK INT UNSIGNED,
ID_ANALISI_IFE INT UNSIGNED,
ID_ANALISI_DATI INT UNSIGNED,
ID_ANALISI_NOTA INT UNSIGNED,
DATA_MODIFICA DATETIME,
ID_UTENTE_MODIFICA SMALLINT UNSIGNED,
DATA_VALIDAZIONE DATETIME,
ID_TIPO_VALIDAZIONE TINYINT UNSIGNED NOT NULL,
ID_UTENTE_VALIDAZIONE SMALLINT UNSIGNED,
DATA_CANCELLAZIONE DATETIME,
ID_UTENTE_CANCELLAZIONE SMALLINT UNSIGNED,
PRIMARY KEY (ID_ANALISI),
INDEX IDX_CAMPIONE (CAMPIONE),
INDEX IDX_REPARTO (REPARTO),
CONSTRAINT FK_ANALISI_PAZIENTE FOREIGN KEY (ID_PAZIENTE) REFERENCES PAZIENTE(ID_PAZIENTE),
CONSTRAINT FK_ANALISI_SESSIONE FOREIGN KEY (ID_SESSIONE) REFERENCES SESSIONE(ID_SESSIONE),
CONSTRAINT FK_ANALISI_PATOLOGICO FOREIGN KEY (ID_PATOLOGICO) REFERENCES PATOLOGICO(ID_PATOLOGICO),
CONSTRAINT FK_ANALISI_TIPO_VALIDAZIONE FOREIGN KEY (ID_TIPO_VALIDAZIONE) REFERENCES TIPO_VALIDAZIONE(ID_TIPO_VALIDAZIONE),
CONSTRAINT FK_ANALISI_UTENTE_MODIFICA FOREIGN KEY (ID_UTENTE_MODIFICA) REFERENCES UTENTE(ID_UTENTE),
CONSTRAINT FK_ANALISI_UTENTE_VALIDAZIONE FOREIGN KEY (ID_UTENTE_VALIDAZIONE) REFERENCES UTENTE(ID_UTENTE),
CONSTRAINT FK_ANALISI_UTENTE_CANCELLAZIONE FOREIGN KEY (ID_UTENTE_CANCELLAZIONE) REFERENCES UTENTE(ID_UTENTE),
CONSTRAINT FK_ANALISI_ANALISI_LINK FOREIGN KEY (ID_ANALISI_LINK) REFERENCES ANALISI(ID_ANALISI),
CONSTRAINT FK_ANALISI_ANALISI_IFE FOREIGN KEY (ID_ANALISI_IFE) REFERENCES ANALISI_IFE(ID_ANALISI_IFE),
CONSTRAINT FK_ANALISI_ANALISI_NOTA FOREIGN KEY (ID_ANALISI_NOTA) REFERENCES ANALISI_NOTA(ID_ANALISI_NOTA),
CONSTRAINT FK_ANALISI_ANALISI_DATI FOREIGN KEY (ID_ANALISI_DATI) REFERENCES ANALISI_DATI(ID_ANALISI_DATI)
)
ENGINE=InnoDB;
CREATE TABLE PAZIENTE
(
ID_PAZIENTE INT UNSIGNED NOT NULL AUTO_INCREMENT,
ID_PAZIENTE_LAB VARCHAR(20),
COGNOME VARCHAR(30),
NOME VARCHAR(30),
DATA_NASCITA DATE,
ID_SESSO TINYINT UNSIGNED NOT NULL,
RECAPITO VARCHAR(50),
CODICE_FISCALE VARCHAR(30),
ID_SPECIE TINYINT UNSIGNED NOT NULL,
PRIMARY KEY (ID_PAZIENTE),
INDEX IDX_DATA_NASCITA (DATA_NASCITA),
INDEX IDX_COGNOME (COGNOME),
INDEX IDX_NOME (NOME),
INDEX IDX_SESSO (ID_SESSO),
CONSTRAINT FK_PAZIENTE_SPECIE FOREIGN KEY (ID_SPECIE) REFERENCES SPECIE(ID_SPECIE),
CONSTRAINT FK_PAZIENTE_SESSO FOREIGN KEY (ID_SESSO) REFERENCES SESSO(ID_SESSO)
)
ENGINE=InnoDB;
In InnoDB every index contains the primary key implicitly.
The explain plan shows that index IDX_NOME is used on table Paziente. The DBMS looks up the name in the index and finds ID_PAZIENTE in there, which is the key we need to access the other table. So there is nothing to add. (In another DBMS we would have added a composite index on (NOME, ID_PAZIENTE) for this to happen.)
Then there is table Analisi to consider. We find a record via FK_ANALISI_PAZIENTE which contains the ID_PAZIENTE which is used to find the match, and implicitly the primary key ID_ANALISI which could be used to access the table, but this is not even necessary, beacuse we have all information we need from the index. There is nothing left that we need to find in the table. (Again, in another DBMS we would have added a composite index on (ID_PAZIENTE, ID_ANALISI) to have a covering index.)
So what happens is merely: read one index in order to read the other index in order to count. Perfect. There is nothing to add.
We could replace COUNT(analisi0_.ID_ANALISI) with COUNT(*) as the former only says "count records where ID_ANALISI is not null", which is always the case as ID_ANALISI is the table's primary key. So it's simpler to use the latter and say "count records". However, I don't expect this to speed up the query significantly if at all.
So from a query point of view, there is nothing to speed this up. Here are further things that come to mind:
Partitioned tables? No, I would see no benefit in this. It could be faster were the query executed in parallel threads then, but as far as I know, there is no parallel execution on multiple partitions in MySQL. (I may be wrong though.)
Defragmenting the tables? No, the tables themselves are not even accessed in the query.
That leaves us with: Buy better hardware. (Sorry not to have any better advice for you.)

MySQL : Error Code 1215 : Cannot add foreign key constraint

I'm trying to make a simple SQL schema, but I'm having some problem with defining foreign keys. I really don't have that much MySQL knowledge, so I thought I'd ask her for some help. I get Error Code 1215 when I try to create the foreign key roomID and 'guestEmail' in the HotelManagement.Reservation table creation.
CREATE database HotelManagement;
CREATE TABLE HotelManagement.Room (
roomID INT not null auto_increment,
roomTaken TINYINT(1),
beds INT not null,
size INT not null,
roomRank INT not null,
PRIMARY KEY(roomID));
CREATE TABLE HotelManagement.HotelTask (
taskType INT not null,
taskStatus TINYINT(1) not null,
whichRoom INT not null,
note VARCHAR(255),
PRIMARY KEY (taskType),
FOREIGN KEY (whichRoom) REFERENCES HotelManagement.Room(roomID));
CREATE TABLE HotelManagement.Guest (
firstName varchar(25) not null,
lastName varchar(25) not null,
userPassword varchar(25) not null,
email varchar(25) not null,
reservation INT,
PRIMARY KEY (userPassword, email));
CREATE TABLE HotelManagement.Reservation (
reservationID INT not null,
id_room INT not null,
guestEmail varchar(25) not null,
fromDate DATE not null,
toDate DATE not null,
PRIMARY KEY (reservationID),
FOREIGN KEY (guestEmail)
REFERENCES HotelManagement.Guest(email),
FOREIGN KEY (id_room)
REFERENCES HotelManagement.Room(roomID)
);
ALTER TABLE HotelManagement.Guest
ADD CONSTRAINT res_constr FOREIGN KEY (reservation)
REFERENCES HotelManagement.Reservation(reservationID);
Updated the .sql
In the hoteltask table you have already defined a foreign key named roomid. Foreign key names also have to be unique, so just give a different name to the 2nd foreign key or omit the name completely:
If the CONSTRAINT symbol clause is given, the symbol value, if used,
must be unique in the database. A duplicate symbol will result in an
error similar to: ERROR 1022 (2300): Can't write; duplicate key in
table '#sql- 464_1'. If the clause is not given, or a symbol is not
included following the CONSTRAINT keyword, a name for the constraint
is created automatically.
UPDATE
The email field in the guest table is the rightmost column of the primary key, this way the pk cannot be used to independently look up email in that table. Either change the order or fields in the pk, or have a separate index on email field in the guest table. Quote from the same link as above:
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.
Pls read through the entire documentation I linked before proceeding with creating the fks!
Side note 2 (1st is in the comments below): you should probably have a unique numeric guest id because that is lot more efficient than using email. Even if you decide to stick with email as id, I would restrict the pk in the guest table to email only. With the current pk I can register with the same email multiple times if I use different password.