SQL: Foreign Key on Column Pointing to Same Column - sql

I've recently come across a bizzare scenario in one of our legacy databases and our DBA (not the one that created it) is uncertain why this would have been done and what benefit it would have. The only thing we can think of is that it was done in error. The following foreign key constraint has been defined on a table:
CREATE TABLE [dbo].[SomeTable]
(
[Id] SMALLINT IDENTITY (1,1) NOT NULL,
-- other columns
CONSTRAINT [PK_SomeTable] PRIMARY KEY CLUSTERED ([Id] ASC)
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[SomeTable] WITH CHECK ADD CONSTRAINT [FK_SomeTable_SomeTable]
FOREIGN KEY ([Id]) REFERENCES [dbo].[SomeTable] ([Id])
GO
ALTER TABLE [dbo].[SomeTable] CHECK CONSTRAINT [FK_SomeTable_SomeTable]
GO
Anyone know or have any thoughts on what this may actually do?

It's possible to use a foreign key that references to the same table for implementing hierarchy, but of course we should use different columns in the same table.
I think the creator of this table just made mistake.

The rows cannot be deleted because of the foreign key constraint. At least SQL Server isn't smart enough to realize that the target of the foreign key is actually the same row. The same goes for trying to edit the value of the primary key.
I guess this could be by design in order to prevent the rows from ever be changed or deleted, but a better solution would be to use triggers instead.

Related

Foreign key constraint cycles or multiple cascade paths

I have a database design like below. I have 3 tables Compartment, CompartmentRelation and CompartmentRelationType . CompartmentRelation table keeps the other compartments around the selected compartment (below,above,behind,infront,etc). CompartmentRelationType keeps the position. Think that i have compartments in the Compartment table named comp-1, comp-2, comp-3, comp-4 and insert the the compartments above comp-1 as comp-2,comp-3 in CompartmentRelation as below. Problem is that setting delete action as cascade for the column RelatedCompId in CompartmentRelation table throw the excaption as
Unable to create relationship 'FK_CompartmentRelation_Compartment1'.
Introducing FOREIGN KEY constraint 'FK_CompartmentRelation_Compartment1' on table 'CompartmentRelation' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
Could not create constraint or index. See previous errors.
Which way should i follow ?
Compartment
comp-1
comp-2
comp-3
comp-4
Compartment Relation
comp-1 -> comp-2
comp-1 -> comp-3
CREATE TABLE [dbo].[Compartment] (
[Id] INT IDENTITY (1, 1) NOT NULL,
[Name] NVARCHAR (500) NOT NULL,
CONSTRAINT [PK_Compartment] PRIMARY KEY CLUSTERED ([Id] ASC),
CREATE TABLE [dbo].[CompartmentRelation] (
[Id] INT IDENTITY (1, 1) NOT NULL,
[CompId] INT NOT NULL,
[RelationTypeId] INT NOT NULL,
[RelatedCompId] INT NOT NULL,
CONSTRAINT [PK_CompartmentRelation] PRIMARY KEY CLUSTERED ([Id] ASC),
CONSTRAINT [FK_CompartmentRelation_CompartmentRelationType] FOREIGN KEY ([RelationTypeId]) REFERENCES [dbo].[CompartmentRelationType] ([Id]) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT [FK_CompartmentRelation_Compartment1] FOREIGN KEY ([RelatedCompId]) REFERENCES [dbo].[Compartment] ([Id]),
CONSTRAINT [FK_CompartmentRelation_Compartment] FOREIGN KEY ([CompId]) REFERENCES [dbo].[Compartment] ([Id]) ON DELETE CASCADE ON UPDATE CASCADE);
CREATE TABLE [dbo].[CompartmentRelationType] (
[Id] INT IDENTITY (1, 1) NOT NULL,
[Name] NVARCHAR (200) NOT NULL,
[NameLan1] NVARCHAR (200) NOT NULL,
[NameLan2] NVARCHAR (200) NULL,
CONSTRAINT [PK_CompartmentRelationType] PRIMARY KEY CLUSTERED ([Id] ASC)
);
Problem is that setting delete action as cascade for the column
RelatedCompId in CompartmentRelation table throw the excaption as
Unable to create relationship
'FK_CompartmentRelation_Compartment1'. Introducing FOREIGN KEY
constraint 'FK_CompartmentRelation_Compartment1' on table
'CompartmentRelation' may cause cycles or multiple cascade paths.
Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other
FOREIGN KEY constraints. Could not create constraint or index.
See previous errors.
The basic issue appears to be that deletes from table Compartment (and updates to its PKs) can cascade to CompartmentRelation through two different foreign keys. If you intend to support Compartments being related to themselves, then that's end-of-story for cascading from Compartment to CompartmentRelation -- you can't do it.* If you intend to forbid self relationships then you could try adding a check constraint to CompartmentRelation to enforce that prohibition, though I'm not at all sure that SQL Server will take that into account.
If SQL Server won't accept the cascading deletes then you have at least three options:
Make it an application responsibility to clean up compartment relationships before deleting compartments. (And don't cascade.)
Create triggers to handle relationship deletion when compartments are deleted. (And don't cascade.)
Create a stored procedure for deleting compartments, and make it handle the needed relationship deletions. (And don't cascade.)
Which way should i follow ?
Whichever of those makes the most sense for your application. All have advantages and disadvantages.
Additionally,
Do not cascade updates of surrogate key columns, especially when the key values are machine generated, as all yours are. Those keys should never be updated in the first place, and if an attempt were ever made to update one then it would be better for the DB to reject it, for whatever reason, than to accept it.
You probably don't want to cascade deletions of CompartmentRelationType to ComponentRelation. Including such cascading allows for deleting all the relations of a given type by deleting the type itself, but such a cascade is more likely to be performed mistakenly than intentionally, and if it were performed mistakenly then the resulting data loss would be significant. It's probably better to make the application delete all those relations explicitly if that's what it really means to do, and otherwise to reject deletion of types that are in use by existing relations.
*Technically, you could do it by cascading from only one of the two FKs with Compartment, but it seems unlikely that such a half-measure would serve your purposes.

Efficiently enforcing a 1:1 relationship between two rows with foreign key constraints without creating redundant unique indexes

I have two PostgreSQL tables designed in the following way:
create type content_owner as enum (
'document',
'task'
);
create table content (
id serial not null primary key,
owner content_owner not null,
owner_document_id int references document(id) deferrable initially deferred,
owner_task_id int references task(id) deferrable initially deferred,
-- ...
constraint collab_content_owner_document
check (owner_document_id is null or (owner = 'document' and owner_document_id is not null)),
constraint collab_content_owner_task
check (owner_task_id is null or (owner = 'task' and owner_task_id is not null))
);
create table document (
id serial not null primary key,
content_id int not null references content(id),
-- ...
);
create table task (
id serial not null primary key,
content_id int not null references content(id),
-- ...
);
I want to enforce a 1:1 relationship at the database level for the document<->content relationship and the task<->content relationship.
Adding the following constraints accomplishes that:
alter table collab_content add foreign key (owner_document_id, id) references document (id, content_id) deferrable initially deferred;
alter table collab_content add foreign key (owner_task_id, id) references task (id, content_id) deferrable initially deferred;
alter table document add foreign key (content_id, id) references collab_content (id, owner_document_id);
alter table task add foreign key (content_id, id) references collab_content (id, owner_task_id);
Since I’m saying the ID pair should reference the same ID pair in the other table for both directions. However, this also requires me to create the following indexes:
alter table document add unique (id, content_id);
alter table task add unique (id, content_id);
alter table collab_content add unique (id, owner_document_id);
alter table collab_content add unique (id, owner_task_id);
These indexes feel pretty redundant given that there’s already a primary key on the id columns for these tables. It feels like PostgreSQL should be smart enough to be able to use the existing primary key constraint to make sure the foreign key constraints are met. Ideally I wouldn’t create a second, redundant, index on these tables for the purpose of these foreign key constraints.
Is there a way for me to avoid creating new unique indexes and instead tell PostgreSQL to only lookup the unique ID when resolving the foreign key?
Will PostgreSQL detect that these unique indexes are redundant (because the first column is the primary key) and not materialize a new index on disk for their purpose?
Is there a better way to enforce this constraint?
Two-way linking like this is a recipe for headaches. I recommend avoiding reference cycles if you can. In your case, the simplest way to store this information is to relax the constraint that there cannot be a content without a document or a task. Ask yourself, how might such a situation occur, how else could it be avoided, and what damage might it cause if it happens?
If we can remove that constraint, then we can have a very simple structure where document and task each have a content_id foreign key, and a unique index on it to ensure that no two documents have the same content.
If we can't remove that constraint, then the answers to your questions are:
There is no way to avoid creating those new unique indexes for the foreign keys. Foreign keys must have matching unique indexes.
Postgres will not detect that these indexes are redundant, and they will indeed be materialized and take up space.

Can FOREIGN KEY be omitted in PostgreSQL when using REFERENCES?

I'm wondering if there's any (maybe subtle) difference between these two SQL statements:
CREATE TABLE profiles (
profile_id SERIAL PRIMARY KEY NOT NULL,
bio TEXT,
user_id INTEGER NOT NULL,
FOREIGN KEY (user_id) REFERENCES users(user_id)
);
and
CREATE TABLE profiles (
profile_id SERIAL PRIMARY KEY NOT NULL,
bio TEXT,
user_id INTEGER NOT NULL REFERENCES users(user_id)
);
I've noticed that when I create a table in Postico with the first notation, but look at the DDL of the created profiles table later, the FOREIGN KEY is removed and I end up with the shorter second notation.
Create table with FOREIGN KEY:
DDL view doesn't show FOREIGN KEY:
So, I'm wondering (and seeking confirmation) that the two statements are in fact 100% equivalent or if there are some subtle differences in what they do to the DB.
Any pointer to official resources (and maybe also how that differs from MySQL) would be appreciated.
The two samples you show do the same thing, just with a different syntax.
The first method is called table constraint, the second column constraint, but the latter name is somewhat misleading because the constraint is on the table as well.
The main difference is that the column constraint syntax is shorter, but cannot be used for all constraints: if you have for example a primary key that contains two columns, you have to write it in the table constraint syntax.
DDL view doesn't show FOREIGN KEY
DDL view created by unknown third-party tool in not an argument.
See fiddle. Foreign key exists in both cases. Moreover, I do not see the result difference for both DDL queries.
PS. As a recommendation - always specify the constraint name explicitly. What if you need to delete it? It is problematic without the constraint name...
In PostgreSQL, you define a foreign key through a foreign key constraint. A foreign key constraint indicates that values in a column or a group of columns in the child table match with the values in a column or a group of columns of the parent table. We say that a foreign key constraint maintains referential integrity between child and parent tables.
This may explain to you better or you can read about Foreign Keys documentation .

error with cross referenced foreign key constraints

I had a weird problem when I am trying to create tables in MySQL.
I want to have a cross reference two many-to-many tables and here is the code I create table
create table teacher(
t_id char(10) not null unique,
name varchar(20) not null,
sur_name varchar(20) not null,
CONSTRAINT pk_teacher PRIMARY KEY(t_id))
create table student(
s_id char(10) not null unique,
name varchar(20) not null,
sur_name varchar(20) not null,
CONSTRAINT pk_student PRIMARY KEY(s_id))
create table teacher_student(
t_id char(10) not null,
s_id char(10) not null,
CONSTRAINT pk_teacher_student PRIMARY KEY(t_id, s_id))
in order to add foreign constraints I used the following code
ALTER TABLE teacher_student
ADD CONSTRAINT fk_teacher_student FOREIGN KEY(s_id) REFERENCES student(s_id)
ALTER TABLE teacher_student
ADD CONSTRAINT fk_student_teacher FOREIGN KEY(t_id) REFERENCES teacher(t_id)
ALTER TABLE student
ADD CONSTRAINT fk_student_teacher_student
FOREIGN KEY(s_id) REFERENCES teacher_student(s_id)
ALTER TABLE teacher
ADD CONSTRAINT fk_teacher_teacher_student
FOREIGN KEY(t_id) REFERENCES teacher_student(t_id)
that works fine but if I try to execute code in a different order like this
ALTER TABLE student
ADD CONSTRAINT fk_student_teacher_student
FOREIGN KEY(s_id) REFERENCES teacher_student(s_id)
ALTER TABLE teacher
ADD CONSTRAINT fk_teacher_teacher_student
FOREIGN KEY(t_id) REFERENCES teacher_student(t_id)
ALTER TABLE teacher_student
ADD CONSTRAINT fk_teacher_student FOREIGN KEY(s_id) REFERENCES student(s_id)
ALTER TABLE teacher_student
ADD CONSTRAINT fk_student_teacher FOREIGN KEY(t_id) REFERENCES teacher(t_id)
ALTER TABLE student
I am getting exception
Can't create table 'test.#sql-44c_37' (errno: 150)
My question is, why the order is important? what is the difference between these two ways of creating constraints? thanks
"I try to execute code in a different order like this" You have no different order you have extremely different constraints. You can't create such constraints.
Examine this article for more information
FOREIGN KEY Constraints
Some of your foreign keys don't seem to make any sense.
Those referencing student and teacher (the fk_teacher_student and fk_student_teacher ones) are fine. That's actually a typical case of a many-to-many table referencing each of the two entity tables whose many-to-many relationship is being implemented.
Now, what are you proposing with referencing back from those tables to the many-to-many one? I admit that I can't really explain why the foreign keys were successfully added using the first script and failed with the other. Sorry about that. Even though I've got some vague idea, that's not really the point of my answer. The point is, your present design is going to make it difficult for you to add new data. With it, you can't really add, say, a student without adding also a relationship between the new student and an existing teacher. This is because a row in student is supposed to reference an existing student in teacher_student, but since it is a new student, there's no corresponding row in teacher_student yet. Same goes for a new teacher.
So, consider just abandoning the idea of fk_student_teacher_student and fk_teacher_teacher_student. They are not needed to your design. They have already caused you to solve an unnecessary problem and they are likely to cause more trouble in the future.

Oracle unique constraint error when inserting

INSERT INTO SS_ALERT_EVENTS ( ALERT_ID, EVENT_ID, TIME_DURATION, ALERT_EVENT_EFFECT, DATASET_ASSIGN_RULE, KEY_FIELDS_ASSIGN_RULE, SIDE, ALERT_VALIDATION_RULE, UNIQUE_ID ) VALUES ( 'test1', 7 , 0, 1 , NULL, '5b414c4552545f494e535452554d454e542e496e737472756d656e742049445d203a3d205b54524144455f5245504f52542e496e737472756d656e742049445d3b', -1, '5b414c4552542e416374696f6e5d203a3d20313b', 1)
*
ERROR at line 1:
ORA-00001: unique constraint (ESV31SURV.PK_SS_ALERT_EVENTS) violated
The EVENT_ID field is the problem. But I want to insert it anyway. However, when I try to drop the constraint of that name, it says there is no such constraint. Further, no such constraint is shown in USER_CONSTRAINTS table. What should I do?
The unique constraint might be in fact be a primary key constraint - at least that's what the name suggests.
Dropping the primary key of a table will potentially have very bad side effect it might break applications that rely on this primary key (and you will also have to drop all foreign keys that reference that table before you can drop the primary key)
That primary key was created with a purposes, so before blindly dropping it you should consult whoever created that schema and make sure that primary key is not needed (or should be redefined).
Having said all this: try to drop the PK using
ALTER TABLE SS_ALERT_EVENTS
DROP PRIMARY KEY
But please double check if this is really a wise decision!