Mysql too slow on simple query between two tables - mysql

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

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.

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

Trouble with SQL 1005 errors (errno 150)

I, like many others, am struggling with the infamous errno 150 in mySQL. I know it has to do with foreign keys, and I know there are already many questions about this issue, but after spending a substantial amount of time searching for a solution I am yet to find one that fits my specific case. Almost every error has been worked out of this travel database, but this final one arises upon trying to create a table entitled leg. I will post below the tables that are involved with this.
CREATE TABLE ticket(
id int UNSIGNED NOT NULL,
passenger_id int UNSIGNED,
trip_id int UNSIGNED,
leg_no int UNSIGNED,
purchased_on datetime,
reservation_date date,
PRIMARY KEY(id),
CONSTRAINT fk_ticket_passenger_id_passenger FOREIGN KEY
(passenger_id) REFERENCES passenger(id),
CONSTRAINT fk_ticket_trip_id_trip FOREIGN KEY
(trip_id) REFERENCES trip(id),
CONSTRAINT fk_ticket_leg_no_leg FOREIGN KEY
(leg_no) REFERENCES leg(leg_no));
CREATE TABLE trip(
id int UNSIGNED NOT NULL,
number_of_legs int UNSIGNED,
PRIMARY KEY(id));
CREATE TABLE leg(
trip_id int UNSIGNED NOT NULL,
leg_no int UNSIGNED NOT NULL,
origin_id int UNSIGNED,
destination_id int UNSIGNED,
depart_time time,
arrive_time time,
vdesignation varchar(255),
price decimal(13,2),
PRIMARY KEY(trip_id, leg_no),
CONSTRAINT fk_leg_trip_id_trip FOREIGN KEY
(trip_id) REFERENCES trip(id),
CONSTRAINT fk_leg_vdesignation_vehicle FOREIGN KEY
(vdesignation) REFERENCES vehicle(designation),
CONSTRAINT fk_leg_origin_id_island FOREIGN KEY
(origin_id) REFERENCES island(id),
CONSTRAINT fk_leg_destination_id_island FOREIGN KEY
(destination_id) REFERENCES island(id));
CREATE TABLE vehicle(
designation varchar(255) NOT NULL,
vtype varchar(255),
capacity int UNSIGNED,
PRIMARY KEY(designation));
CREATE TABLE island(
id int UNSIGNED NOT NULL,
iname varchar(255),
can_fly tinyint UNSIGNED,
PRIMARY KEY(id));
I am aware that this question gets asked a lot, and I'm sure it gets old, I will be incredibly appreciative of any advice that you may have for me. I've searched quite thoroughly for the solution and cannot seem to figure it out. Thanks ahead of time for any info you can give me!
EDIT: Here is the specific warning info for the error:
*************************** 1. row ***************************
Level: Error
Code: 1005
Message: Can't create table 'travel.leg' (errno: 150)
1 row in set (0.04 sec)
EDIT 2: Added the vehicle table in response to the first answer, because somehow I forgot to add it in the first place.
I see two potential problems with the foreign keys in your leg table:
1. The first problem is with the fk_leg_vdesignation foreign key
CONSTRAINT fk_leg_vdesignation_vehicle FOREIGN KEY (vdesignation)
REFERENCES vehicle(designation)
You never showed us the vehicle table, so we can't verify that the designation column there as the same type as leg.vdesignation, which is varchar(255). If the types don't match, or if vehicle.designation does not exist, you should get the 150 error you are seeing.
2. The following two foreign keys in leg both appear to be referencing the same column in the island table:
CONSTRAINT fk_leg_origin_id_island FOREIGN KEY (origin_id) REFERENCES island(id)
CONSTRAINT fk_leg_destination_id_island FOREIGN KEY (destination_id) REFERENCES island(id)
I don't see the point of having both of them. Furthermore, the type of leg.origin_id and leg.destination_id is int UNSIGNED, while the type of the foreign column island.id is int UNSIGNED NOT NULL. Since the types are not exactly the same, this could be causing the error 150.
I ended up figuring it out via a sudden burst of intuition. The issue actually came from the ticket table. To resolve the issue, I combined the final two foreign keys of the table. This means that I changed my constraints from this:
CONSTRAINT fk_ticket_trip_id_trip FOREIGN KEY
(trip_id) REFERENCES trip(id),
CONSTRAINT fk_ticket_leg_no_leg FOREIGN KEY
(leg_no) REFERENCES leg(leg_no));
To this:
CONSTRAINT fk_ticket_leg_no_leg FOREIGN KEY
(trip_id, leg_no) REFERENCES leg(trip_id, leg_no));
My understanding of what went wrong is that a foreign key is supposed to reference a primary key of another table. When the second of the two foreign keys referred to leg.leg_no, it was only referring to part of the key. leg.leg_no is a prime attribute from the primary key, but on its own cannot identify a particular row of the table, so it will not function properly as a foreign key reference.
Thank you anyway to Tim for helping me! Without your comment about having two foreign keys refer to the same key, I may never have thought of this.

Should the normal Key include the primary key?

This question is related to
Do MySQL tables need an ID?
There is a meaningless auto_incremental ID acting as PRIMARY KEY for a table, then when I create other KEYs, should I include this ID in the KEYs?
For example, in this table:
CREATE TABLE `location` (
`ID` int(11) NOT NULL AUTO_INCREMENT,
`country` varchar(50),
`states` varchar(50),
`city` varchar(50),
`county` varchar(50),
`zip` int(5),
PRIMARY KEY(ID),
KEY zip1 (zip),
KEY zip2 (zip, ID)
} ENGINE=InnoDB ;
Because I need to search the table using zip code a lot, so I need a KEY start from zip code. I should use either KEY zip1 or KEY zip2. Which one of these two KEYs is better?
For InnoDB, the primary key is always included in secondary indexes;
All indexes other than the clustered index are known as secondary indexes. In InnoDB, each record in a secondary index contains the primary key columns for the row, as well as the columns specified for the secondary index. InnoDB uses this primary key value to search for the row in the clustered index.
In other words, ID is already included in zip1, and does not have to be mentioned as it is in in zip2.

Primary and foreign key in SQL?

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.