May cause cycles or multiple cascade paths - sql

I have a task:
If a "shop" is deleted all references to it is set to NULL.
When I try to create a table:
CREATE TABLE TEST
(
id int Primary Key,
shop int FOREIGN KEY REFERENCES TEST(id) ON DELETE SET NULL,
);
I get an error:
Introducing FOREIGN KEY constraint 'FK__TEST__shop__2882FE7D' on table 'TEST' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
Msg 1750, Level 16, State 0, Line 1
What am I doing wrong?

You are getting an error because you are creating a table with a reference to itself. This is not a good idea, and also this is not what you want.
For your task, you need to create another table, with a relationship (a foreign key) that references your first table. The foreign key must be defined properly to hold the rule that sets the child to NULL when the parent is deleted, like :
CONSTRAINT fk_name
FOREIGN KEY (child_col)
REFERENCES parent_table (parent_col)
ON DELETE SET NULL
See this link on how to set option ON DELETE in a foreign key

You can do this, but not exactly as you wish. The following works:
CREATE TABLE TEST (
id int Primary Key,
shop int,
FOREIGN KEY (shop) REFERENCES TEST(id) ON DELETE NO ACTION,
);
To be honest, I'm not sure why the in-line definition doesn't work. The more important point is the action. The SET NULL is not allowed, because SQL Server is very cautious about potential cycles. However, NO ACTION is allowed.

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.

I have a problem with this query in Postgres

I have a problem with this table in Postgres, it give me this error:
ERROR: cannot use subquery in check constraint
LINE 66: check(Artista in(Select ID_Artista
create table DirigeF(
Artista int references Artista(ID_Artista) on delete cascade,
Film int references Film(ID_Contenuto) on delete cascade,
check(Artista in(Select ID_Artista
from Artista
where tipologia='REGISTA'or'AR')),
constraint DirigeF_PK primary key(Artista, Film)
);
I want to check that Artista in table DirigeF has tipologia='REGISTA' from another table.
As the error suggests, you cannot do this with a check constraint. One option is a trigger. Another is a foreign key constraint -- but that needs to be carefully arranged.
First you need a column that indicates whether the type in Artista is "REGISTA" or "AR". That would be:
alter table artista add is_regista_ar bool generated always as
(tipologia in ('REGISTA', 'AR'));
Then create a unique constraint or index:
alter table artista add unq_artista_tipologia_id
unique (is_regista_ar, id_artista)
Note: This requires Postgres 12+. But something similar can be done in earlier versions.
Then, add a boolean column to your table that is always true:
create table DirigeF (
Artista int references Artista(ID_Artista) on delete cascade,
Film int references Film(ID_Contenuto) on delete cascade,
is_regista_ar bool generated always as true,
constraint fk_artista_tipo_artista foreign key (is_regista_ar, Artista) references Artista(is_regista_ar, ID_Artista),
constraint DirigeF_PK primary key (Artista, Film)
);

Will a constraint be applied between two statements during a transaction?

I have a configuration that a thing can have multiple properties and a property can belong to multiple things:
CREATE TABLE thing (
id integer NOT NULL,
PRIMARY KEY (id)
);
CREATE TABLE property (
id integer NOT NULL,
PRIMARY KEY (id)
);
CREATE TABLE thingproperty (
thing integer NOT NULL,
property integer NOT NULL
);
ALTER TABLE thingproperty
ADD CONSTRAINT tp_thing
FOREIGN KEY (thing) REFERENCES thing(id)
ON UPDATE CASCADE ON DELETE CASCADE;
ALTER TABLE thingproperty
ADD CONSTRAINT tp_property
FOREIGN KEY (property) REFERENCES property(id)
ON UPDATE CASCADE ON DELETE CASCADE;
I'd like to ensure that a property can only exist if it belongs to at least one thing, and for this I wrote this transaction that removes things (and then also properties if necessary) but I don't know whether it's correct:
START TRANSACTION;
DELETE FROM thing ... ;
DELETE FROM property
WHERE id NOT IN (
SELECT property
FROM thingproperty
);
COMMIT TRANSACTION;
So I basically trusted the engine that after the DELETE FROM thing ... query had run, it immediately applies the tp_thing constraint and removes the record(s) of thingproperty belonging to the deleted thing(s) before DELETE FROM property ... is executed.
Is this a safe way to do it?
By default constraints are applied immediately after every command in a transaction. You can (in this case you do not want to) change this behaviour declaring a constraint as deferrable. Read more about deferrable constraints in the documentation.
DEFERRABLE
NOT DEFERRABLE
This controls whether the constraint can be deferred. A constraint that is not deferrable will be checked immediately after every command. (...)

Syntax Error for Referential Triggered Actions in Oracle

I am trying to create a new table with "on delete" and "on update" referential triggered actions, but it keeps saying that there is a syntax error. When I tried to run the script, the error message is "missing right parentheses". I don't understand why this is not right.
CREATE TABLE PERSON
(SSN VARCHAR(9) NOT NULL,
Name VARCHAR(20) NOT NULL,
Phone VARCHAR(10),
Address VARCHAR(40),
Employer VARCHAR(20),
Insurer VARCHAR(20),
PCP INT,
CONSTRAINT PERPPK
PRIMARY KEY(SSN),
CONSTRAINT PEREMPFK
FOREIGN KEY(Employer) REFERENCES EMPLOYER(Name)
ON DELETE SET NULL ON UPDATE CASCADE,
CONSTRAINT PERINSFK
FOREIGN KEY(Insurer) REFERENCES INSURER(Name)
ON DELETE SET NULL ON UPDATE CASCADE,
CONSTRAINT PERPCP
FOREIGN KEY(PCP) REFERENCES PHYSICIAN(PID)
ON DELETE SET NULL ON UPDATE CASCADE);
ON UPDATE CASCADE is not valid syntax. There is no direct way to do this and for good reason. You should not be creating a model where you have to update values in Primary keys. Primary keys should only be created and dropped, never updated. If it is being updated, then it is probably not a primary key. But that is a design consideration that you would have to make.
If you want to implement something like cascading updates, you could maybe do it with a stored procedure and deferrable constraints. Defer the constraint and update both the parent and foreign keys before committing.
Here is the asktom discussion on this. Another.

Why is this a cyclical foreign key constraint?

I came upon this code, marked "error," in an application I'm to update. Running it on a test database gives a cyclical reference error:
The referential relationship will result in a cyclical reference that is not allowed (Constraint name = descriptions_fk_2)
I named the constraints to see which one caused the problem.
CREATE TABLE items (
id INT NOT NULL UNIQUE IDENTITY,
name NCHAR(100) NOT NULL UNIQUE,
PRIMARY KEY (id)
);
CREATE TABLE sources (
id INT NOT NULL UNIQUE IDENTITY,
item_id INT NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY (item_id)
REFERENCES items(id) ON UPDATE NO ACTION ON DELETE CASCADE
);
CREATE TABLE descriptions (
id INT NOT NULL UNIQUE IDENTITY,
item_id INT NOT NULL,
source_id INT NOT NULL,
PRIMARY KEY (id),
CONSTRAINT descriptions_fk_1 FOREIGN KEY (item_id)
REFERENCES items(id) ON UPDATE NO ACTION ON DELETE CASCADE,
CONSTRAINT descriptions_fk_2 FOREIGN KEY (source_id)
REFERENCES sources(id) ON UPDATE NO ACTION ON DELETE CASCADE
);
Why is this a cyclical reference? The descriptions table is linked to two separate tables, but none of them link back to descriptions.
It's not strictly cyclical - but there are multiple cascade paths. So you could cascade delete a row in items two ways:
1) description -> item
2) description -> source -> item
And, for that reason, it's disallowed.
I believe it's a performance concern, as PostGres will allow cycles like that and will just work it out, but deletes under those circumstances can be quite slow.
For some further reading about why it's disallowed, please see this answer.