Les clés primaires sont automatiquement indexée (sauf sur les vieux SGBD).
Il est très intéressant de faire des indexes portant sur plusieurs colonnes, car pour une requête donnée, un seul index pourra être utilisé par table.
Donc si une requête possède deux filtres, par exemple :
couleur = 'Rouge' et poids > 2
A ce moment, il faudra un index sur les deux champs à la fois pour profiter pleinement de l'index.
Sinon, pour ce qui est des primary key, ce sont les clés.
Elles peuvent porter sur un unique champ (unique id) ou un sur plusieurs champs (doublets, triplets, etc.)
Par exemple, pour un utilisateur, on aura une clé primaire "user_id" ou "login", selon le choix, qui est unique et forcément remplis pour chaque utilisateurs.
Il y aura aussi une clé alternative, qui portera sur le couplet "login/password", afin d'interdire que deux utilisateurs aient les mêmes login/pass. Une dernière clé alternative pourra porter sur l'email, afin d'éviter que deux utilisateurs utilisent la même email.
Les clés en tant que telles sont de moins en moins utilisées. On utilise généralement des index uniques, qui permettent de faire les mêmes chose, mais avec un certain nombre de vérifications en moins, ce qui accélère la base. Il est cependant recommendé de toujours définir au moins une clé primaire par table (les index uniques ne seront utilisés que pour les clés alternatives).
Pour ce qui est des "foreign key", elles servent à indiquer que les valeurs d'un champ proviennent d'une autre table. Cela correcpond à un lien 1,n ou 0,n en MERISE.
Cela permettra notamment d'interdire de créer un produit avec comme code famille "PAPER" si aucune famille de cet id n'est déclarée, ou aussi, ça interdira de supprimer une famille tant qu'elle contiendra des membres.
Les clés étrangères créent aussi des indexes, qui permettent d'optimiser considérablement les jointures. Avec une clé primaire correctement créé, on peut même se passer de rappeler le champ de jointure dans la requête, même si c'est absoluement déconseillé.
Par exemple :
si la table "produit" à comme clé étrangère "fam_id" qui pointe sur l'"id" de la table "famille", alors les deux requêtes soivantes reviennent au même :
select famille.fam_nom from famille, produit where produit.poids > 2
select famille.fam_nom from famille, produit where famille.id = produit.fam_id and produit.poids > 2
Par contre ! Si la clé étrangère n'est pas créé, la première requête retournera une ligne de famille par ligne de produit valide, car le SGBD sera incapable de retrouver le lien entre les deux tables. Il est donc très fortement déconseillé d'omettre les champs de jointure, même s'ils ne sont pas indispensables dans le cas d'un table correcte.
Pour terminer, une dernière petite subtilité :
soit une table "utilisateur", une table "article", et une table "vote".
La table utilisateur a pour clé primaire "user_id".
Article et Vote ont comme clé étrangère "user_id", qui pointe sur "user_id" de "utilisateur".
Un article est écrit par un utilisateur.
Un vote est effectué par des utlisateurs à propos d'un article.
Mettons que tu veux retrouver le moyenne des votes pour l'article "1", sans prendre en compte le vote de l'auteur de l'article.
Tu vas écrire :
select avg(vote.note)
from vote, article
where article.id = 1
and vote.user_id <> article.user_id
Si "user_id" des deux tables n'est pas déclaré en tant que clé étrangère, mais juste indexé, alors pour "article", l'optimiseur ne saura s'il doit prendre l'index qui pointe sur "id" ou "user_id" dans la table article. Les perfs seront donc moyennes.
Si ces deux champs sont convenablement déclarés comme clé étrangères, alors l'optimiseur va intégré lui-même la table "utilisateur" à le requête, et utiliser automatiquement les indexs correspondant aux clés étrangères. Il pourra dont en plus utilise l'index pointant sur "article.id". Les performances seront donc très largement suppérieures.