T-SQL: foreign key that's not referencing a primary key - tsql

I have the following database:
CREATE TABLE ContentNodes
(
Id UNIQUEIDENTIFIER NOT NULL,
Revision INT IDENTITY(1,1) NOT NULL,
ParentId UNIQUEIDENTIFIER NULL
PRIMARY KEY (Id, Revision)
)
How do I limit ParentId to only contain values from the Id column. Trying to make ParentId a foreign key gives me:
PRINT 'FK_ContentNodes_ParentId_ContentNodes';
ALTER TABLE ContentNodes
ADD CONSTRAINT FK_ContentNodes_ParentId_ContentNodes FOREIGN KEY (ParentId) REFERENCES ContentNodes(Id);
GO
Error:
There are no primary or candidate keys in the referenced table
'ContentNodes' that match the
referencing column list in the foreign
key
'FK_ContentNodes_ParentId_ContentNodes'.

Since you have a compound primary key (Id, Revision) on your ContentNodes, you have to use both columns in a foreign key relation.
You cannot reference only parts of a primary key - simply cannot be done.
You have to either introduce a surrogate primary key into your table which is just a simple INT IDENTITY and then you can self-reference that single PK column, or you can (if it's possible in your data model) put a UNIQUE INDEX on that one column you want to reference:
CREATE UNIQUE NONCLUSTERED INDEX UIX_ID
ON ContentNodes(Id)
Once you have a UNIQUE INDEX on that column, then you can use it as a FK reference.

Related

there is no unique constraint matching given keys for referenced table "category"?

I'm trying to define more than one foreign key in the process table. but I am getting the error that the columns I am trying to define as foreign key are not 'unique value'.
For this, I wanted to define id and name columns as primary keys in category and subject tables. However, when I want to create the process table, I still get this error." there is no unique constraint matching given keys for referenced table "category"
I have researched and continue to do so on Stackoverflow and many more. but I couldn't figure it out with solutions or viewpoints of the issues that got the same error I was facing. Maybe there is something I'm not seeing.
first table;
CREATE TABLE category(
category_id INT GENERATED ALWAYS AS IDENTITY,
category_name VARCHAR(210),
category_description TEXT,
CONSTRAINT category_pk PRIMARY KEY(category_id,category_name)
);
second table;
CREATE TABLE subject(
subject_id INT GENERATED ALWAYS AS IDENTITY,
subject_name VARCHAR(210),
subject_description TEXT,
CONSTRAINT subject_pk PRIMARY KEY(subject_id,subject_name)
);
I tried that too but I keep getting the same error
ALTER TABLE category ADD CONSTRAINT some_constraint PRIMARY KEY(category_id,category_name);
third table;
CREATE TABLE process(
process_id INT GENERATED ALWAYS AS IDENTITY,
fk_category_id INTEGER,
fk_subject_id INTEGER,
FOREIGN KEY(fk_category_id) REFERENCES category(category_id) ON DELETE CASCADE ON UPDATE
CASCADE,
FOREIGN KEY(fk_subject_id) REFERENCES subject(subject_id) ON DELETE CASCADE ON UPDATE
CASCADE
);
In your FOREIGN KEY declaration either:
Include both the columns that make up the PRIMARY KEY on category and subject e.g. ... REFERENCES category(category_id, category_name) ...
OR
Do not refer to any column and let the FK pick up the PK automatically e.g. ... REFERENCES category ON DELETE ....
I am going to say that what you are really after is:
CREATE TABLE category(
category_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
category_name VARCHAR(210) UNIQUE,
category_description TEXT
);
CREATE TABLE subject(
subject_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
subject_name VARCHAR(210) UNIQUE,
subject_description TEXT
);
CREATE TABLE process(
process_id INT GENERATED ALWAYS AS IDENTITY,
fk_category_id INTEGER,
fk_subject_id INTEGER,
FOREIGN KEY(fk_category_id) REFERENCES category ON DELETE CASCADE ON UPDATE
CASCADE,
FOREIGN KEY(fk_subject_id) REFERENCES subject ON DELETE CASCADE ON UPDATE
CASCADE
);
An identity column isn't automatically a primary key. So your tables category and subjectdon't have any primary keys and thus can't be referenced by a foreign key.
You need to add PRIMARY KEY to the columns of the tables category and subject

Citus: How can I add self referencing table in distributed tables list

I'm trying to run create_distributed_table for tables which i need to shard and almost all of the tables have self relation ( parent child )
but when I run SELECT create_distributed_table('table-name','id');
it throws error cannot create foreign key constraint
simple steps to reproduce
CREATE TABLE TEST (
ID TEXT NOT NULL,
NAME CHARACTER VARYING(255) NOT NULL,
PARENT_ID TEXT
);
ALTER TABLE TEST ADD CONSTRAINT TEST_PK PRIMARY KEY (ID);
ALTER TABLE TEST ADD CONSTRAINT TEST_PARENT_FK FOREIGN KEY (PARENT_ID) REFERENCES TEST (ID);
ERROR
citus=> SELECT create_distributed_table('test','id');
ERROR: cannot create foreign key constraint
DETAIL: Foreign keys are supported in two cases, either in between two colocated tables including partition column in the same ordinal in the both tables or from distributed to reference tables
For the time being, it is not possible to shard a table on PostgreSQL without dropping the self referencing foreign key constraints, or altering them to include a separate and new distribution column.
Citus places records into shards based on the hash values of the distribution column values. It is most likely the case that the hashes of parent and child id values are different and hence the records should be stored in different shards, and possibly on different worker nodes. PostgreSQL does not have a mechanism to create foreign key constraints that reference records on different PostgreSQL clusters.
Consider adding a new column tenant_id and adding this column to the primary key and foreign key constraints.
CREATE TABLE TEST (
tenant_id INT NOT NULL,
id TEXT NOT NULL,
name CHARACTER VARYING(255) NOT NULL,
parent_id TEXT NOT NULL,
FOREIGN KEY (tenant_id, parent_id) REFERENCES test(tenant_id, id),
PRIMARY KEY (tenant_id, id)
);
SELECT create_distributed_table('test','tenant_id');
Note that parent and child should always be in the same tenant for this to work.

Do i need index for composite primary key where each column is foreign key

When i create table user:
pk
id | int
And table friend:
pk, fk | pk, fk
user_id | friend_id
As i understand composite pk creates unique index user_id+friend_id internally and this index is used for user_id foreign key. But do i need to create index for friend_id because index on composite pk friend table will not work for friend_id (it's second in composite pk)?
SQL:
CREATE TABLE public."user"
(
id integer NOT NULL DEFAULT nextval('user_id_seq'::regclass),
)
CREATE TABLE public.user_friend
(
user_id integer NOT NULL,
friend_id integer NOT NULL,
CONSTRAINT user_friend_pkey PRIMARY KEY (user_id, friend_id),
CONSTRAINT user_friend_friend_id_fkey FOREIGN KEY (friend_id)
REFERENCES public."user" (id) MATCH SIMPLE
ON UPDATE CASCADE
ON DELETE CASCADE,
CONSTRAINT user_friend_user_id_fkey FOREIGN KEY (user_id)
REFERENCES public."user" (id) MATCH SIMPLE
ON UPDATE CASCADE
ON DELETE CASCADE
)
CREATE INDEX fki_user_friend_friend_id_fkey
ON public.user_friend USING btree
(friend_id ASC NULLS LAST)
TABLESPACE pg_default;
As i understand composite pk creates unique index user_id+friend_id intenally
Yes.
and this index is used for user_id foreign key.
Not exactly, no.
There are two cases here:
an INSERT or UPDATE on the user_friend table. It will have to check the table's primary key of course (and will use the user_friend_pkey index for that). For the foreign key constraints, this will only have to check whether the user_id and friend_id values exist in the user table, and will use the index created by user's primary key for those.
an UPDATE or DELETE on the user table. It will have to check whether the old value was referenced in the user_friend table, and act according to the CASCADE or RESTRICT strategy. To find this row in user_friend, it will use the indices on user_id and on friend_id if they exist.
But do i need to create index for friend_id because index on composite pk friend table will not work for friend_id
Yes, looking up by friend_id is slow without the extra index. You only need this if you want to find users that have x as their friend, or when updates/deletes on user must not be slow.

PostgreSQL: Insert violates foreign key constraint

I'm working on a project for which I want to use inheritance (see code below)
When I try to insert something into Profile there is an error.
ERROR: insert or update on table "profile" violates foreign key
constraint "profile_id_fkey"
DETAIL: Key (id)=(21) is not present in table "test".
I'm using PSequel to inspect the database and the values are visible in the parent. However, I am still able to insert a duplicate primary key in the parent. The insert then works.
CREATE TABLE Test (
ID INTEGER NOT NULL,
PRIMARY KEY (ID)
);
CREATE TABLE Testchild (
PRIMARY KEY (ID)
) INHERITS (Test)
;
CREATE TABLE Profile (
ProfileID INTEGER NOT NULL,
ID INT NOT NULL,
PRIMARY KEY (ProfileID)
);
ALTER TABLE Profile
ADD FOREIGN KEY (ID) REFERENCES Test(ID);
INSERT INTO Testchild VALUES (21);
INSERT INTO Profile VALUES (1,21);
From PostgreSQL documentation:
A serious limitation of the inheritance feature is that indexes (including unique constraints) and foreign key constraints only apply to single tables, not to their inheritance children. This is true on both the referencing and referenced sides of a foreign key constraint. Thus, in the terms of the above example:
https://www.postgresql.org/docs/9.1/static/ddl-inherit.html
So you have to declare explicitly any FK, they wont inherit

Will a primary key index serve as an index for a foreign key when fk columns are subset of pk?

I have a table where part of the primary key is a foreign key to another table.
create table player_result (
event_id integer not null,
pub_time timestamp not null,
name_key varchar(128) not null,
email_address varchar(128),
withdrawn boolean not null,
place integer,
realized_values hstore,
primary key (event_id, pub_time, name_key),
foreign key (email_address) references email(address),
foreign key (event_id, pub_time) references event_publish(event_id, pub_time));
Will the index generated for the primary key suffice to back the foreign key on event_id and pub_time?
Yes.
Index A,B,C
is good for:
A
A,B
A,B,C (and any other combination of the full 3 fields, if default order is unimportant)
but not good for other combinations (such as B,C, C,A etc.).
It will be useful for the referencing side, such that a DELETE or UPDATE on the referenced table can use the PRIMARY KEY of the referencing side as an index when performing checks for the existence of referencing rows or running cascade update/deletes. PostgreSQL doesn't require this index to exist at all, it just makes foreign key constraint checks faster if it is there.
It is not sufficient to serve as the unique constraint for a reference to those columns. You couldn't create a FOREIGN KEY that REFERENCES player_result(event_id, pub_time) because there is no unique constraint on those columns. That pair can appear multiple times in the table so long as each pair has a different name_key.
As #xagyg accurately notes, the unique b-tree index created by the foreign key reference is also only useful for references to columns from the left of the index. It could not be used for a lookup of pub_time, name_key or just name_key, for example.