web-dev-qa-db-fra.com

Terminologie MySQL "contraintes" vs différence "clés étrangères"?

Je regarde les documents MySQL ici et j'essaie de faire la distinction entre les clés étrangères et les contraintes. Je pensais qu'un FK était une contrainte, mais les docs semblent en parler comme s'ils étaient séparés.

La syntaxe pour créer un FK est (en partie) ...

[CONSTRAINT [symbol]] FOREIGN KEY
    [index_name] (index_col_name, ...)
    REFERENCES tbl_name (index_col_name,...)

La clause "CONSTRAINT" est donc optionnelle. Pourquoi voudriez-vous l'inclure ou ne pas l'inclure? Si vous le laissez pas, MySQL crée-t-il une clé étrangère mais pas une contrainte? Ou est-ce plutôt comme si "CONSTRAINT" n'était rien d'autre qu'un nom pour votre FK, alors si vous ne le spécifiez pas, vous obtenez un FK anonyme?

Toute clarification serait grandement appréciée.

Merci,

Ethan

43
Ethan

Oui, une clé étrangère est un type de contrainte. MySQL supporte de manière inégale les contraintes:

  • PRIMARY KEY: yes en tant que contrainte de table et contrainte de colonne.
  • FOREIGN KEY: oui comme contrainte de table, mais uniquement avec les moteurs de stockage InnoDB et BDB; sinon analysé mais ignoré.
  • CHECK: analysé mais ignoré dans tous les moteurs de stockage.
  • UNIQUE: yes en tant que contrainte de table et contrainte de colonne.
  • NOT NULL: oui comme contrainte de colonne.
  • DEFERRABLE et autres attributs de contrainte: pas de support. 

La clause CONSTRAINT vous permet de nommer explicitement la contrainte, soit pour rendre les métadonnées plus lisibles, soit pour utiliser le nom lorsque vous souhaitez supprimer la contrainte. Le standard SQL exige que la clause CONSTRAINT soit facultative. Si vous le laissez pas, le SGBDR crée un nom automatiquement et ce nom appartient à l'implémentation.

58
Bill Karwin

En général (pas nécessairement MySQL), les clés étrangères sont des contraintes, mais les contraintes ne sont pas toujours des clés étrangères. Pensez aux contraintes principales, aux contraintes uniques, etc.

Pour en revenir à la question spécifique, vous avez raison, omettre la partie CONSTRAINT [symbol] créera un FK avec un nom généré automatiquement.

9
Dan C.

A ce stade, nos DDL CREATE TABLE sont de ce format. Notez la syntaxe de définition UNIQUE KEY et FOREIGN KEY que nous avons utilisée. 

CREATE TABLE my_dbschema.my_table (
    id INT unsigned auto_increment PRIMARY KEY,
    account_nbr INT NOT NULL,
    account_name VARCHAR(50) NOT NULL,
    active_flg CHAR(1) NOT NULL DEFAULT 'Y',
    vendor_nbr INT NOT NULL,
    create_ts TIMESTAMP NOT NULL DEFAULT current_timestamp,
    create_usr_id VARCHAR(10) NOT NULL DEFAULT 'DFLTUSR',
    last_upd_ts TIMESTAMP NOT NULL DEFAULT current_timestamp ON UPDATE current_timestamp,
    last_upd_usr_id VARCHAR(10) NOT NULL DEFAULT 'DFLTUSR',
    UNIQUE KEY uk1_my_table(account_nbr, account_name),
    FOREIGN KEY fk1_my_table(vendor_nbr) REFERENCES vendor(vendor_nbr)
    );

Dans ce format, MySQL crée automatiquement INDEX-es avec les noms uk1_my_table et fk1_my_table. mais le nom de l'objet FK est différent - my_table_ibfk_1 (c'est-à-dire. tablename_ibfk_N - défini par le système). Donc, ALTER TABLE my_table DROP FOREIGN KEY fk1_my_table ne fonctionnera pas (et donc frustrant et déclenchera des alarmes), car il n’ya pas d’objet de base de données FK portant ce nom.

Voici un autre format DDL pour les constarints (Ref: https://dev.mysql.com/doc/refman/5.6/fr/create-table-foreign-keys.html ): -

CREATE TABLE my_dbschema.my_table (
    id INT unsigned auto_increment PRIMARY KEY,
    account_nbr INT NOT NULL,
    account_name VARCHAR(50) NOT NULL,
    active_flg CHAR(1) NOT NULL DEFAULT 'Y',
    vendor_nbr INT NOT NULL,
    create_ts TIMESTAMP NOT NULL DEFAULT current_timestamp,
    create_usr_id VARCHAR(10) NOT NULL DEFAULT 'DFLTUSR',
    last_upd_ts TIMESTAMP NOT NULL DEFAULT current_timestamp ON UPDATE current_timestamp,
    last_upd_usr_id VARCHAR(10) NOT NULL DEFAULT 'DFLTUSR',
    CONSTRAINT uk1_my_table UNIQUE KEY (account_nbr, account_name),
    CONSTRAINT fk1_my_table FOREIGN KEY (vendor_nbr) REFERENCES vendor(vendor_nbr)
    );

Dans ce format, MySQL crée toujours INDEX-es avec les noms uk1_my_table et fk1_my_table automatiquement, mais le nom de l’objet FK n’est pas différent, c’est fk1_my_table comme indiqué dans la DDL. Donc, ALTER TABLE my_table DROP FOREIGN KEY fk1_my_table fonctionne, mais laisse derrière lui le nom INDEX. 

Et notez que ALTER TABLE my_table DROP INDEX fk1_my_table ne fonctionnera pas initialement (lorsque le FK n’est pas encore supprimé), avec un message d’erreur indiquant qu’il est utilisé dans un FK! Si la commande DROP FK a été exécutée avec succès, alors DROP INDEX fonctionne. 

J'espère que cela explique et aide à résoudre la confusion.

2
RxBx

Si je ne me trompe pas, les contraintes ont besoin d'index. Ainsi, lorsque vous créez, par exemple, une contrainte de clé étrangère, MySQL crée automatiquement un index.

1
Mario Juárez

Je ne peux pas répondre pour MySQL, mais les FK sont des contraintes. Tout ce qui force vos données dans certaines conditions est une contrainte. Il existe plusieurs types de contraintes. Unique, clé primaire, clé de contrôle et clé étrangère sont toutes des contraintes. Peut-être que MySQL en a d'autres.

Parfois, les mots sont autorisés dans les commandes mais ne sont pas obligatoires pour des raisons de lisibilité, comme le dans la déclaration DELETE.

1
Mark Brady

C'est probablement la topique la plus déroutante de MySQL. 

Beaucoup de gens disent que, par exemple, les clés 'PRIMARY KEY', 'FOREIGN KEY' et 'UNIQUE' sont en fait des index! (La documentation officielle de MySQL est incluse ici)

Beaucoup d'autres, en revanche, disent qu'il s'agit plutôt de contraintes (ce qui est logique, car lorsque vous les utilisez, vous imposez vraiment des restrictions aux colonnes affectées).

Si ce sont vraiment des index, à quoi sert-il d'utiliser la clause de contrainte pour lui donner un nom, puisque vous êtes censé pouvoir utiliser le nom de cet index lors de sa création?

Exemple:

... FOREIGN KEY nom_index (nom_colonne1, nom_colonne2, ...)

Si FOREIGN KEY est un index, nous devrions pouvoir utiliser le nom_index pour le gérer. Cependant, nous ne pouvons pas.

Mais s’ils ne sont pas des index mais des contraintes réelles qui utilisent des index pour fonctionner, cela a du sens.

En tout cas, on ne sait pas. En fait, personne ne semble savoir.

0
Johann