Quelle est la cause d'ERREUR: il n'y a pas de contrainte unique correspondant aux clés données pour la table référencée?

155

L'exemple de structure de table ci-dessous donne une ERREUR: il n'y a pas de contrainte unique correspondant aux clés données pour la table référencée, et après l'avoir regardée pendant un moment, je ne peux pas comprendre pourquoi cette erreur se produit dans cette situation.

BEGIN;

CREATE TABLE foo (
    name                VARCHAR(256) PRIMARY KEY
);

CREATE TABLE bar(
    pkey        SERIAL PRIMARY KEY,
    foo_fk      VARCHAR(256) NOT NULL REFERENCES foo(name), 
    name        VARCHAR(256) NOT NULL, 
    UNIQUE (foo_fk,name)
);

CREATE TABLE baz(   
    pkey            SERIAL PRIMARY KEY,
    bar_fk          VARCHAR(256) NOT NULL REFERENCES bar(name),
    name            VARCHAR(256)
);

COMMIT;

L'exécution du code ci-dessus donne l'erreur suivante, ce qui n'a aucun sens pour moi, quelqu'un peut-il expliquer pourquoi cette erreur se produit. J'utilise postgres 9.1

NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "foo_pkey" for table "foo"
NOTICE:  CREATE TABLE will create implicit sequence "bar_pkey_seq" for serial column "bar.pkey"
NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "bar_pkey" for table "bar"
NOTICE:  CREATE TABLE / UNIQUE will create implicit index "bar_foo_fk_name_key" for table "bar"
NOTICE:  CREATE TABLE will create implicit sequence "baz_pkey_seq" for serial column "baz.pkey"
NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "baz_pkey" for table "baz"
ERROR:  there is no unique constraint matching given keys for referenced table "bar"


********** Error **********

ERROR: there is no unique constraint matching given keys for referenced table "bar"
SQL state: 42830
ams
la source

Réponses:

189

C'est parce que la namecolonne de la bartable n'a pas la contrainte UNIQUE .

Alors imaginez que vous avez 2 lignes sur la bartable qui contiennent le nom 'ams'et que vous insérez une ligne bazavec 'ams'on bar_fk, à quelle ligne barferait-il référence puisqu'il y a deux lignes correspondantes?

Diego
la source
1
parfaite explication courte et précise et facile à saisir!
Alex le
79

Dans postgresql, toutes les clés étrangères doivent référencer une clé unique dans la table parent, donc dans votre bartable, vous devez avoir un unique (name)index.

Voir également http://www.postgresql.org/docs/9.1/static/ddl-constraints.html#DDL-CONSTRAINTS-FK et plus précisément:

Enfin, nous devons mentionner qu'une clé étrangère doit référencer des colonnes qui sont soit une clé primaire, soit une contrainte unique.

Soulignez le mien.

Matteo Tassinari
la source
21
pourquoi le PK déclaré n'est-il pas considéré comme une contrainte unique? ce n'est pas comme si vous pouviez avoir un PK non unique ...
amphibient
2
Il doit être unique sur la table vers laquelle il "pointe", car si ce n'est pas le cas, le moteur de base de données n'aura aucun moyen de savoir à quelle ligne vous faites réellement référence.
Matteo Tassinari
Clés composites? @amphibient
Charming Robot
1
Je pense qu'avoir une clé unique sur la colonne référencée sur la table parent n'est pas nécessaire dans postgresql seulement mais aussi dans d'autres SGBDR comme oracle, serveur sql etc.
Mufachir Hossain
2
Notez que la réponse est également vraie pour les clés étrangères composites, où une contrainte unique composite ou une clé primaire est requise sur la table parent.
Ninjakannon
8

quand vous faites UNIQUEcomme une contrainte au niveau de la table comme vous l'avez fait alors ce que votre définition est un peu comme une clé primaire composite voir les contraintes ddl , voici un extrait

"This specifies that the *combination* of values in the indicated columns is unique across the whole table, though any one of the columns need not be (and ordinarily isn't) unique."

cela signifie que l'un ou l'autre des champs peut avoir une valeur non unique à condition que la combinaison soit unique et que cela ne corresponde pas à votre contrainte de clé étrangère.

il est fort probable que vous souhaitiez que la contrainte soit au niveau de la colonne. Ainsi plutôt que de les définir comme des contraintes au niveau de la table, «ajoutez» UNIQUEà la fin de la définition de colonne comme name VARCHAR(60) NOT NULL UNIQUEou spécifiez des contraintes de niveau de table individuelles pour chaque champ.

TI
la source
Contrainte de niveau colonne dans ma situation ne fonctionnera pas , je devrais vraiment être une clé primaire en train de définir composé, mais je soutenu loin parce que la cartographie à JPA est un peu d'une douleur :)
Ams
6

Vous devez avoir la colonne de nom comme contrainte unique. voici 3 lignes de code pour changer vos problèmes

  1. Découvrez d'abord les contraintes de clé primaire en tapant ce code

    \d table_name

    vous êtes montré comme ça en bas "some_constraint" PRIMARY KEY, btree (column)

  2. Supprimez la contrainte:

    ALTER TABLE table_name DROP CONSTRAINT some_constraint
  3. Ajoutez une nouvelle colonne de clé primaire avec une colonne existante:

    ALTER TABLE table_name ADD CONSTRAINT some_constraint PRIMARY KEY(COLUMN_NAME1,COLUMN_NAME2);

C'est tout.

Hari Bharathi
la source