JSON foreign keys in PostgreSQL - postgresql

Is it possible to assign a foreign key to a json property in PostgreSQL? Here is an example what I would like to achieve, but it doesn't work:
CREATE TABLE Users (Id int NOT NULL PRIMARY KEY);
CREATE TABLE Data (
Id int NOT NULL PRIMARY KEY,
JsonData json NOT NULL, -- [{Id: 1, somedata: null},{Id: 2, somedata: null}, ...]
CONSTRAINT FK_Users_Data FOREIGN KEY (JsonData->Id) REFERENCES Users(Id) -- this constraint will fail
);

It is not possible, and may not ever be possible, to assign a foreign key to a json property. It'd be a major and quite complicated change to PostgreSQL's foreign key enforcement. I don't think it's impossible to do, but would face similar issues to those experienced by the foreign-keys-to-arrays patch.
With 9.4 it'll be possible to make a whole json object a foreign key as jsonb supports equality tests. In 9.3 you can't even do that.

Here's a little SPI function have_ids which I use for an integrity constraint on a one-to-many relationship with a jsonb column
CREATE TABLE foo (
id INTEGER NOT NULL
)
CREATE TABLE bar (
foo_ids pg_catalog.jsonb DEFAULT '[]'::jsonb NOT NULL,
CONSTRAINT bar_fooids_chk CHECK (have_ids ('foo', foo_ids))
)
With a couple of triggers on foo it's almost as good as a foreign key.

The foreign key parameter must be a column name:
http://www.postgresql.org/docs/current/static/sql-createtable.html
You will have to normalize
create table user_data (
id int not null primary key,
user_id int not null,
somedata text,
constraint fk_users_data foreign key (user_id) references Users(Id)
);

Yes it is possible but you will have to store another value. If you change your schema to:
CREATE TABLE Users (Id int NOT NULL PRIMARY KEY);
CREATE TABLE Data (
Id int NOT NULL PRIMARY KEY,
JsonData json NOT NULL,
UserId int generated always as ((JsonData->>'Id')::int) stored references Users(Id)
);
INSERT INTO Users VALUES (1);
Foreign key that doesn't exist:
INSERT INTO Data VALUES (1, '{"Id": 3}');
Returns the error:
ERROR: insert or update on table "data" violates foreign key constraint "data_userid_fkey" DETAIL: Key (userid)=(3) is not present in table "users".
Foreign key that does work:
INSERT INTO Data VALUES (1, '{"Id": 1}');

Related

DB2 referential constraint cannot created

I can not add a referential constraint, both tables has the two columns id and version as primary key. I will only check the integrity that the row is existing without checking version.
CREATE TABLE TABLEE
(ID INTEGER NOT NULL,
VERSION INTEGER NOT NULL,
PRIMARY KEY (ID , VERSION);
CREATE TABLE CHAIR
(ID INTEGER NOT NULL,
VERSION INTEGER NOT NULL,
ID_TABLEE INTEGER,
PRIMARY KEY (ID , VERSION);
ALTER Chair
ADD constraint MYC FOREIGN KEY (ID)
REFERENCES TABLEE (ID)
ON DELETE RESTRICT
Got this error
[Code: -573, SQL State: 42890] A column list specified in the references clause of constraint "MYC " does not identify a unique constraint of the parent table or nickname "TABLEE".. SQLCODE=-573, SQLSTATE=42890, DRIVER=4.28.11
The problem here is that Tablee its primary key consists of two columns id and version. But the integrity check should only be made on the referenced id, not version.
To establish a foreign key on a table the referenced column needs to:
Be a primary key on the other table.
...or at least act as one.
Since you already have a primary key on the referenced table, you can use the second approach and add a UNIQUE and NOT NULL constraints on ID.
For example:
CREATE TABLE TABLEE (
ID INTEGER NOT NULL,
VERSION INTEGER NOT NULL,
PRIMARY KEY (ID , VERSION),
constraint uq1 unique (id) -- added UNIQUE constraint on ID
);
CREATE TABLE CHAIR (
ID INTEGER NOT NULL,
VERSION INTEGER NOT NULL,
ID_TABLEE INTEGER,
PRIMARY KEY (ID , VERSION)
);
ALTER table Chair
ADD constraint MYC FOREIGN KEY (ID)
REFERENCES TABLEE (ID)
ON DELETE RESTRICT;
See running example at db<>fiddle.
Note: If you want ID to have repeated values over the table, then ID is not a key, and cannot be referenced as one.

PostgreSQL/pgAdmin4 ERROR: there is no unique constraint matching given keys for referenced table "index"

I'm trying to transform my MySQL design to PostgreSQL but when I try to create the table "index":
CREATE TABLE "model1"."index" (
"id_index" INT GENERATED ALWAYS AS IDENTITY ,
"index_name" VARCHAR(5) NOT NULL,
"index_type_id_index_type" INT NOT NULL,
"index_provider_id_index_provider" INT NOT NULL,
"miseq" VARCHAR(45) NOT NULL,
"nextseq" VARCHAR(45) NOT NULL,
PRIMARY KEY ("id_index"),
CONSTRAINT "fk_index_index_type"
FOREIGN KEY ("index_type_id_index_type")
REFERENCES "model1"."index_type" ("id_index_type")
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT "fk_index_index_provider"
FOREIGN KEY ("index_provider_id_index_provider")
REFERENCES "model1"."index_provider" ("id_index_provider")
ON DELETE NO ACTION
ON UPDATE NO ACTION);
I got this error: ERROR: there is no unique constraint matching given keys for referenced table "index".
The two tables containing the foreign keys were created before the "index" table:
CREATE TABLE "model1"."index_type" (
"id_index_type" INT GENERATED ALWAYS AS IDENTITY ,
"name" VARCHAR(3) NOT NULL,
PRIMARY KEY ("id_index_type"));
CREATE TABLE "model1"."index_provider" (
"id_index_provider" INT GENERATED ALWAYS AS IDENTITY ,
"name" VARCHAR(40) NOT NULL,
PRIMARY KEY ("id_index_provider"));
Solved. The error was in another table as #a_horse_with_no_name suggested.

Error Code: 1822. Failed to add the foreign key constraint. Missing index for constraint 'questions_ibfk_1' in the referenced table 'category'

cannot add foreign key constraint to table
create table users
(
user_id int auto_increment primary key not null,
username varchar(50) unique null ,
email varchar(50) unique ,
passwords varchar(50) not null,
login_status boolean not null
);
create table category (
category_id int primary key not null,
category_name varchar(50) not null
);
create table answers (
id_answer int auto_increment primary key not null,
answer boolean not null
);
create table questions (
question_id int primary key not null,
category_name varchar(50) not null,
content varchar(50) not null ,
foreign key (category_name) references category (category_name)
);
You get this error because there's no index on category_name in the category table. Change that CREATE statement as follows:
create table category (
category_id int primary key not null,
category_name varchar(50) not null,
KEY category_name_index (category_name)
);
From the docs (8.0 version, but the statement is true for older versions):
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.
Also, you're using a varchar(50) as your foreign key, which is not usually a great idea for a variety of reasons. You probably want to use a numeric value, such as category_id, instead.

How to add a foreign key for table B by referring of table A and foreign key for table A by referring table B in PostgreSQL?

I need to add a foreign keys for two tables each other. Can this be done ?
As an example:
CREATE SCHEMA IF NOT EXISTS schema1;
CREATE TABLE schema1.tableA
(
id serial NOT NULL,
tableB_id integer,
PRIMARY KEY (id),
FOREIGN KEY (tableB_id) REFERENCES schema1.tableB (id)
);
CREATE TABLE schema1.tableB
(
id serial NOT NULL,
tableA_id integer,
PRIMARY KEY (id),
FOREIGN KEY (tableA_id) REFERENCES schema1.tableA(id)
);
Above query causes an error !
ERROR: relation "schema1.tableb" does not exist SQL state: 42P01
Can this be done or is there any better solution?
I'am using PostgreSQL version 10.5 and pgAdmin 3.6
Add the foreign keys after creating both tables:
CREATE SCHEMA IF NOT EXISTS schema1;
CREATE TABLE schema1.tableA
(
id serial NOT NULL,
tableB_id integer,
PRIMARY KEY (id)
);
CREATE TABLE schema1.tableB
(
id serial NOT NULL,
tableA_id integer,
PRIMARY KEY (id)
);
ALTER TABLE schema1.tablea
add FOREIGN KEY (tableB_id) REFERENCES schema1.tableB (id);
ALTER TABLE schema1.tableb
add foreign key (tableA_id) REFERENCES schema1.tableA(id);

composite primary key of a foreign key

This is a sample of my code. I'm using sql server 2008 R2
create table Entidade(
NIF numeric(9,0) primary key,
nome varchar(250) not null,
tipoEnt varchar(4) check (tipoEnt in ('CE','EC','CEEC',null))
)
create table Curso(
cod int primary key identity(1,1),
descricao text,
nHoras decimal(5,2),
NIFEnt numeric(9,0) references Entidade(NIF),
)
create table AccaoFormacao(
ref int identity(1,1) not null,
codCr int references Curso(cod) not null,
dtInicio date,
dtFim date,
BIFDR numeric(8,0) references Formador(BI),
constraint pkAccaoFormacao primary key(ref, codCr),
)
create table AF_FDO(
codCr int,
refAf int,
BI numeric(8,0) foreign key references Formando(BI),
constraint fkAF_FDO foreign key(codCr, refAf) references AccaoFormacao(codCr, ref),
constraint pkAF_FDO primary key(codCr, refAf, BI)
)
Everything goes fine until I try to create the table AF_FDO, it says:
"Msg 1776, Level 16, State 0, Line 1
There are no primary or candidate keys in the referenced table 'AccaoFormacao' that match the referencing column list in the foreign key 'fkAF_FDO'."
I understand the message but can't figure how to fix it since I'm declaring the primary key in the constraint pkAccaoFormacao it makes no sense to me.
The unique constraint in AccaoFormacao(codCr, ref) works but at the same time it doesn't "fit" in what I'm doing here. AccaoFormacao is a weak entity of Curso thus I have a composite primary key in AccaoFormacao.
EDIT1:
Ok I figured it out after hours I feels stupid.
I changed the constraint fkAF_FDO foreign key(codCr, refAf) references AccaoFormacao(codCr, ref) in AF_FDO to constraint fkAF_FDO foreign key(codCr, refAf) references AccaoFormacao and it worked. It will get the composite primary key of AccaoFormacao as it should, the other way should also work...
Thanks for the help anyway.
There is no PK in AccaoFormacao , take a look at Ref and CodCr , FKS needs to point a PK.