Restrict delete of master record if it has children - sql

How can I restrict delete of a master record if it has children in a Firebird database.

You need to add a foreign key from the child table to the parent table. The default behavior of a foreign key will prevent deletion of records from the parent table if there are child records.
For example
create table parent (
id integer generated by default as identity primary key
);
create table child (
id integer generated by default as identity primary key,
parent_id integer references parent(id)
);
This will also prevent you from adding records to child with parent_id values (other than null) that do not exist in parent. You can further modify the behavior of the foreign key constraint using on update and on delete clauses. See the Firebird language reference on constraints. For example using on delete cascade will delete rows from the child table if the parent record is deleted.
A foreign key can only point to a primary key or a unique key.
The above code is intentionally short, you should consider using named constraints for both primary and foreign key constraints, as it will simplify future maintenance, check the language reference for details.

Related

MySQL parent child constraint

I have this table which has a parent-child structure
CREATE TABLE `testable` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`parent_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
I now want to make sure every parent_id references a valid id.
Then I want to have an ON UPDATE CASCADE ON DELETE CASCADE on my id column so that if I change an id, every same parent_id gets updated automatically, and same for delete.
I've tried with the basic approach as follows:
ALTER TABLE `testable`
ADD CONSTRAINT `fk_testable_1`
FOREIGN KEY (`parent_id`)
REFERENCES `testable` (`id`)
ON DELETE CASCADE
ON UPDATE CASCADE;
I can then correctly try to update a row's parent_id with a non-existant id and the constraint blocks me.
The problem is then when I try to update an id which is referenced in some parent_id, there I get the following error:
ERROR 1451: 1451: Cannot delete or update a parent row: a foreign key constraint fails
The expected behaviour I want to achieve is my row id to change, and every row with that parent_id to update to reference the new id
Worth noting that parent_id is nullable and this is a table I use to create a dynamic side menu with sub-menus for my application.

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.

How to manage postgresql foreign keys?

I need some advice on SQL structure on Postgresql.
I have those two tables :
DROP TABLE IF EXISTS "public"."attribute_value";
CREATE TABLE "public"."attribute_value"
(
"id" INTEGER NOT NULL,
"attribute_id" INTEGER NOT NULL,
"value" CHARACTER VARYING(100) NULL
);
--*****************************************************
DROP TABLE IF EXISTS "public"."product_attribute";
CREATE TABLE "public"."product_attribute"
(
"product_id" INTEGER NOT NULL,
"attribute_value_id" INTEGER NOT NULL,
"attribute_id" INTEGER NOT NULL
);
I added no constraints on purpose.
I need a foreign key on the child table product_attribute.attribute_value_id referencing the parent table attribute_value.id. The best practice is to create a primary key on the field attribute_value.id (maybe with a sequence), or to CREATE UNIQUE INDEX on attribute_value.id ?
I first thought indexes were only special lookup tables that the database search engine can use to speed up data retrieval. But when I played with foreign keys, I found that creating an unique index allowed me to avoid error "there is no unique constraint matching given keys for referenced table blablabla" because a foreign key is not supposed to point to a non unique value. Should indexes be used to create foreign keys then ?
I also need a foreign key on the child table product_attribute.attribute_id referencing parent table attribute_value.attribute_id. The problem is that attribute_value.attribute_id is not unique. But all the rows in product_attribute.attribute_id must not take any value out of attribute_value.attribute_id's possible values. How should I do ?
Every table should have a primary key. Don't join the legion of people who complain about duplicate rows in their tables.
So make id the primary key of attribute_value, then you can create a foreign key constraint.
Constraints are implemented by unique indexes, so technically they are almost the same. Still you need a constraint and not just a unique index as the target of a foreign key constraint.
About attribute_id: that should not be a foreign key constraint between the two tables from your question, but both tables should have a foreign key referencing yet another table (attribute?).

May cause cycles or multiple cascade paths

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.

Why this UNIQUE constraint is needed?

I am trying to design schema in PostgreSQL that will contain two tables cross referencing each other. Yet if do not add redundant UNIQUE constrain (see code below) i am getting error: ERROR: there is no unique constraint matching given keys for referenced table "nodes".
So my question is: why this extra unique constraint is needed and is there a way to avoid it creation? (to reduce runtime overhead).
CREATE TABLE objects (
object_id serial NOT NULL PRIMARY KEY,
root_node integer
);
CREATE TABLE nodes (
node_id integer NOT NULL PRIMARY KEY,
object_id integer REFERENCES objects
);
ALTER TABLE objects
ADD CONSTRAINT root_node_fkey
FOREIGN KEY (root_node) REFERENCES nodes(node_id);
-- Why this constaint is needed? Since node_id is primary key this combination should be already UNIQUE
ALTER TABLE nodes ADD CONSTRAINT node_id_object_id_unique UNIQUE (node_id, object_id);
ALTER TABLE objects
ADD CONSTRAINT objects_nodes_fkey
FOREIGN KEY (object_id, root_node)
REFERENCES nodes (object_id, node_id);
https://www.postgresql.org/docs/current/static/ddl-constraints.html says:
5.3.5. Foreign Keys:
. . .
A foreign key must reference columns that either are a primary key or form a unique constraint. This means that the referenced columns always have an index (the one underlying the primary key or unique constraint); so checks on whether a referencing row has a match will be efficient.
https://mariadb.com/kb/en/sql-99/constraint_type-foreign-key-constraint/ says:
A FOREIGN KEY Constraint is either a < Table Constraint> or a and defines a rule that constrains a foreign key to values that match only those values contained in a referenced unique key.
Re your comment:
The idea is that each object will have collections of nodes associated with it and only one of nodes could be the root-node.
ALTER TABLE objects
ADD COLUMN root_node_id integer,
ADD CONSTRAINT objects_nodes_fkey
FOREIGN KEY (root_node_id)
REFERENCES nodes (node_id);
That way each object references exactly one node. Admittedly, constraint doesn't strictly enforce that an object references a node that references the same object.
If you want that level of enforcement, you'll have to create the unique constraint you were asking if you had to.