Postgresql Foreign Key Actions - Delete Attribute and Change Other Attributes Related to This - sql

I create 3 tables just like image. Each students can be enrolled multiple class I tried to build one to many relation.
What I want to do is, when a student is deleted from the "Student" table, the course in which the student is registered in the "Bridge" table returns to null. How can I do this operations with postgresql (pgAdmin 4), can you help me please? Thank you...

You are describing the on delete set null option to foreign keys constraints. The create table statement for bridge would look like:
create table bridge (
std_id int references students(std_id) on delete set null,
class_id int references class(class_id)
);
I am unsure that set null is your best pick for such a bridge table though. This leaves "gaps" in your data that do not make a lot of sense. on delete cascade would probably make more sense - and you could apply it to both foreign keys:
create table bridge (
std_id int references students(std_id) on delete cascade,
class_id int references class(class_id) on delete cascade
);
That way, the bridge table is properly cleaned up when any parent record is dropped. This also opens the way to set up a composite primary key made of both columns in the bridge table.

Related

How do I create a bridge table? Do I create two primary keys and two foreign keys?

I have a table that I want to make which is a bridge table for teacher and class table. This is the ERD
I initially thought I'm going to create this table by
CREATE TABLE class_teacher
(
teacher_id number(3),
class_id number(2),
CONSTRAINT class_teacher_pk
PRIMARY KEY(teacher_id, class_id),
CONSTRAINT class_teacher_teacher_fk
FOREIGN KEY(teacher_id) REFERENCES teacher(teacher_id),
CONSTRAINT class_teacher_class_fk
FOREIGN KEY(class_id) REFERENCES class(class_id)
);
But on the web I see people just having two foreign keys and no primary key, or table with no foreign key and having a primary key for two columns.
Am I doing it incorrectly?
Am I doing it incorrectly?
No, it looks correct.
Although I would question the size of the numeric data types as you are restricted to only have 1999 teachers and 199 classes (including negative numbers); this may be enough for immediate use but after several years when classes get re-organised or when the syllabus is re-written and new classes are created then you may run out of primary keys.
Does create statement returns any error? Otherwise you should be good.
Try to insert some data in all 3 tables and run some delete statements to see how it goes.

Postgres many to many relationship on same base table

I'm trying to setup a many to many association on the same base table in postgres.
I'm a little stuck with this query; particularly the following error message.
Error
ERROR: insert or update on table "link" violates foreign key
constraint "link_primaryid_fkey" SQL state: 23503 Detail: Key
(primaryid)=(2) is not present in table "entity".
I would expect the PrimaryId column to exist in the Link table (Which it does). However the error seems to suggest the PrimaryId column also needs to exist in the base Entity Table which is what I'm trying to avoid.
Would anyone be able to point me in the right direction?
Script to get up to speed
--- Create Tables
CREATE TABLE "Entity" ("Id" SERIAL PRIMARY KEY);
CREATE TABLE "Task" ("Name" TEXT) INHERITS ("Entity");
CREATE TABLE "Project" ("Name" TEXT) INHERITS ("Entity");
--- Create mock data
INSERT INTO "Task" ("Name") VALUES ('Foo');
INSERT INTO "Project" ("Name") VALUES ('Bar');
-- Create Link Table
CREATE TABLE "Link" (
"PrimaryId" INTEGER REFERENCES "Entity" ("Id")
ON UPDATE CASCADE
ON DELETE CASCADE,
"SecondaryId" INTEGER REFERENCES "Entity"("Id" )
ON UPDATE CASCADE
ON DELETE CASCADE,
PRIMARY KEY ("PrimaryId", "SecondaryId")
);
--- Create Associations. It errors here
INSERT INTO "Link" ("PrimaryId", "SecondaryId") VALUES (2, 1)
Update 1
Updated to use absolute case (") everywhere
Thanks
Foreign keys do not work well with inheritance, as they only see the contents of the base table, not the inherited tables.
Please see https://www.postgresql.org/docs/9.6/static/ddl-inherit.html and specifically:
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:
(...)
Specifying that another table's column REFERENCES cities(name) would allow the other table to contain city names, but not capital names. There is no good workaround for this case.

Delete entry in table that is referenced by another table

I have two tables, Games and Sport_Games. Sport_Games has a foreign key that references Games.
Now when I want to delete an entry from Games I get an error saying:
"FK__Sport_Gam__game___1D7C2B7C". The conflict occurred in database "DatabasesProject", table "dbo.Sport_Games", column 'game_id'.
The statement has been terminated.
Im assuming this is because I can't delete something from a table with a constraint without dropping it. I tried adding ON DELETE CASCADE in table Games in hopes that when I delete a game it is also deleted from Sport_Games but that didn't work either.
Here are the tables for reference:
CREATE TABLE Games(
game_id INT IDENTITY PRIMARY KEY,
name VARCHAR(50),
release_date date,
rating INT,
min_age INT,
development_team_email VARCHAR(50) FOREIGN KEY REFERENCES Development_Teams,
release_conference INT FOREIGN KEY REFERENCES Conferences
--ON DELETE CASCADE ---this is what I added
)
CREATE TABLE Sport_Games(
game_id INT PRIMARY KEY FOREIGN KEY REFERENCES Games,
sport_type VARCHAR(50),
)
I have other tables referencing Games as well, action_games, strategy_games, But they're similar to sport_games
If you know the GameID of the records you are deleting, simply delete records in the Sport_Games table that have the same GameID first, then delete from Games table.
For the cascade to work, you need to ad that to the FK definition on the Sport_Games table. That way when the Games record is deleted, the sport_Games record will be deleted as well.
Your ON DELETE CASCADE will work for you if you put it on the correct Foreign Key. But to answer your question...
Since Games.Game_ID has a constraining reference against Sport_Games.Game_ID, you need to either turn the constraint off so it will not prevent you from deleting, but this will leave orphans, or delete the children before you delete the parent. Since you must already know the PK value to delete the parent, you simply:
DELETE FROM Sport_Games WHERE Game_ID = [The id you are using to delete from Games]
Then you delete the parent:
DELETE FROM Games WHERE Game_ID = [Same id used above]
What I'm about to say might not directly answer your question, but it could be beneficial for you moving forward or in future designs.
I've found placing "delete_date" and "delete_id" in tables to be a useful way to work around these FK constraint issues. If your application is only calling existing stored procedures and table functions you could update the queries to include "AND delete_date IS NULL" in their conditions. If your application is writing adhoc queries, it might also be as simple as creating or modifying a base class to inject the condition that omits soft-deleted rows.

What is the simplest way to delete a child row when its parent is deleted, without knowing what its parent is?

Given multiple entity types:
Cluster
Hypervisor
VirtualMachine
and given properties that could belong to any one of them (but no more than one per row):
CpuInfo
CpuSpeed
CpuTotal
...
DataStore
...
What is the simplest way to delete a property with its parent?
Attempted Solutions
ON DELETE CASCADE
ON DELETE CASCADE seems to require a nullable foreign key for each possible parent, which strikes me as a poor design:
CREATE TABLE CpuInfo
(
-- Properties
Id INT IDENTITY(1,1) NOT NULL PRIMARY KEY,
CpuSpeed INT,
AllocatedTotal INT,
CpuTotal INT,
AvailableTotal INT,
-- Foreign keys for all possible parents
ClusterId INT,
HypervisorId INT,
VirtualMachineId INT,
FOREIGN KEY (ClusterId) REFERENCES Cluster(Id) ON DELETE CASCADE,
FOREIGN KEY (HypervisorId) REFERENCES Hypervisor(Id) ON DELETE CASCADE,
FOREIGN KEY (VirtualMachineId) REFERENCES VirtualMachine(Id) ON DELETE CASCADE
);
Junction Tables with Triggers
Parents are related to properties through junction tables. For example:
CREATE TABLE HypervisorCpuInfo
(
HypervisorId INT NOT NULL,
CpuInfoId INT NOT NULL,
FOREIGN KEY (HypervisorId) REFERENCES Hypervisor(Id),
FOREIGN KEY (CpuInfoId) REFERENCES CpuInfo(Id) ON DELETE CASCADE
);
There is then a DELETE trigger for each entity type. The trigger selects the IDs of the entity's properties and deletes them. When the properties are deleted, the child junction rows are then deleted also, via ON CASCADE DELETE.
This doesn't model the business rules very well, though, since it allows the same CpuInfo to belong to multiple entities. It also adds a lot of tables to the design.
Is there a simpler solution?
I think a "junction table" might be fitting for DRYness (it isn't a real junction because of the 1:n relation)
You could call your "junction table" a "super table" (something like "machine" [sorry I'm not native]):
In this table you put all the keys to your properties (make each foreign key column unique to ensure 1:1*). The very type of your "machine" (Cluster,Hypervisor,VirtualMachine) is in the "triple key" you already tried - also in the super-table.
To ensure "machine" is only of one entity add a constraint:
ALTER TABLE CpuInfo WITH CHECK ADD CONSTRAINT [CK_keyIDs] CHECK (
(ClusterId IS NULL AND HypervisorId IS NULL AND VirtualMachineId IS NOT NULL)
OR (ClusterId IS NULL AND HypervisorId IS NOT NULL AND VirtualMachineId IS NULL)
OR (ClusterId IS NOT NULL AND HypervisorId IS NULL AND VirtualMachineId IS NULL)) GO
The good thing is you are quite free with your entities, you could allow a PC to be a Cluster at the same time.
*the key-column! the ID already has to be unique

Should a foreign key be created on the parent table or child table?

What's the difference? If I have these two tables:
CREATE TABLE Account (Id int NOT NULL)
CREATE TABLE Customer (AccountId int NOT NULL)
And I want a foreign key linking the two, which of the following should I do and why?
Option 1:
ALTER TABLE [dbo].[Customer] WITH CHECK
ADD CONSTRAINT [FK_Accounts_Customers] FOREIGN KEY([AccountId])
REFERENCES [dbo].[Account] ([Id])
Option 2:
ALTER TABLE [dbo].[Account] WITH CHECK
ADD CONSTRAINT [FK_Accounts_Customers] FOREIGN KEY([Id])
REFERENCES [dbo].[Customer] ([Id])
Depends on context. Does every customer have a client? Which one is the parent? It seems like an Account has multiple Customers, in which case the reference belongs on the Customer table.
Now, that said, please call the entities CustomerID and AccountID everywhere. It may seem redundant on the primary table but the name should be consistent throughout the model.
I would use a foreign key from the child to the parent. The tell tale question is: what happens if you need to delete one of the entities?
A FK (foreign key) tells the DBMS that values for subrows for a column list must appear elsewhere as values for subrows for a column list. Whenever that happens (and it isn't already implied by other declartions) declare the FK. If in addtion you want a CASCADE action applied to the referenced table on a change to the referencing table, declare that.
(There's nothing special about CASCADE that it couldn't be offered for non-FK situations. It just comes up frequently with FKs, and there's an explicit graph of FKs by which to reasonably restrict their interactions.)
If there is a FK cycle then you will need to use triggers. Your decision of which constraint(s) are enforced declaratively & which by trigger should consider the graph of (desired) constraints.