RDBMS Ref. Integrity: A child with too many parents? - rdbms

I have a general design question. Consider these 3 tables:
Table Restaurants:
RID pk auto_increment
etc...
Table Vacations:
VID pk auto_increment
etc...
Table Movies:
MID pk auto_increment
etc...
And now imagine we want to create a list "Top things to do when COVID is over" of selected records from these 3 different tables. The list may contain any mix of records from these tables. What comes to mind then is:
Table Todo:
Type [ one of R, V, M ]
ID [ the ID of the parent item ]
But how would you enforce referential integrity on this thing? I.e., how do we ensure that when a restaurant is deleted from Restaurants, it will also drop from Todo?
(I am aware of how to accomplish these things with triggers; Curious if there's a combination of entities that will accomplish this with pure RDBMS ref. int.)
Thank you!

You can add nullable foreign key columns in your todo table for each target table you have. So your table will look like:
Table Todo:
RID fk (nullable)
VID fk (nullable)
MID fk (nullable)
The type column isn't needed anymore as you can check which column is filled with a foreign key. Obviously you have to add a CHECK constraint to ensure that exactly one foreign key must be set.

Related

understanding an inheritance in Postgres; why key "fails" in insert/update command

(One image, tousands of words)
I'd made few tables that are inherited between themselves. (persons)
And then assign child table (address), and relate it only to "base" table (person).
When try to insert in child table, and record is related to inherited table, insert statement fail because there is no key in master table.
And as I insert records in descendant tables, records are salo available in base table (so, IMHO, should be visible/accessible in inherited tables).
Please take a look on attached image. Obviously do someting wrong or didn't get some point....
Thank You in advanced!
Sorry, that's how Postgres table inheritance works. 5.10.1 Caveats explains.
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.
In their example, capitals inherits from cities as organization_employees inherits from person. If person_address REFERENCES person(idt_person) it will not see entries in organization_employees.
Inheritance is not as useful as it seems, and it's not a way to avoid joins. This can be better done with a join table with some extra columns. It's unclear why an organization would inherit from a person.
person
id bigserial primary key
name text not null
verified boolean not null default false
vat_nr text
foto bytea
# An organization is not a person
organization
id bigserial not null
name text not null
# Joins a person with an organization
# Stores information about that relationship
organization_employee
person_id bigint not null references person(id)
organization_id bigint not null references organization(id)
usr text
pwd text
# Get each employee, their name, and their org's name.
select
person.name
organization.name
from
organization_employee
join person on person_id = person.id
join organization on organization_id = organization.id
Use bigserial (bigint) for primary keys, 2 billion comes faster than you think
Don't enshrine arbitrary business rules in the schema, like how long a name can be. You're not saving any space by limiting it, and every time the business rule changes you have to alter your schema. Use the text type. Enforce arbitrary limits in the application or as constraints.
idt_table_name primary keys makes for long, inconsistent column names hard to guess. Why is the primary key of person_address not idt_person_address? Why is the primary key of organization_employee idt_person? You can't tell, at a glance, which is the primary key and which is a foreign key. You still need to prepend the column name to disambiguate; for example, if you join person with person_address you need person.idt_person and person_address.idt_person. Confusing and redundant. id (or idt if you prefer) makes it obvious what the primary key is and clearly differentiates it from table_id (or idt_table) foreign keys. SQL already has the means to resolve ambiguities: person.id.

How to edit a record that results in a uniqueness violation and echo the change to child tables?

PostgreSQL 11.1
How can "Editing" of a record be transmitted to dependent records?
Summary of my issue:
The master table, disease, needs a unique constraint on the description column. This unique constraint is needed for foreign key ON UPDATE CASCADE to its children tables.
To allow for a temporary violation of the unique constraint, it must be made deferrable. BUT A DEFERABLE CONSTRAINT CAN NOT BE USED IN A FOREIGN KEY.
Here is the situation.
The database has 100+ tables (and keeps on growing).
Most all information has been normalized in that repeating groups of information have been delegated to their own table.
Following normalization, most tables are lists without duplication of records. Duplication of records within a table is not allowed.
All tables have a unique ID assigned to each record (in addition to a unique constraint placed on the record information).
Most tables are dependent on another table. The foreign keys reference the primary key of the table they are dependent on.
Most unique constraints involve a foreign key (which in turn references the primary key of the parent table).
So, assume the following schema:
CREATE TABLE phoenix.disease
(
recid integer NOT NULL DEFAULT nextval('disease_recid_seq'::regclass),
code text COLLATE pg_catalog."default",
description text COLLATE pg_catalog."default" NOT NULL,
CONSTRAINT disease_pkey PRIMARY KEY (recid),
CONSTRAINT disease_code_unique UNIQUE (code)
DEFERRABLE,
CONSTRAINT disease_description_unique UNIQUE (description)
,
CONSTRAINT disease_description_check CHECK (description <> ''::text)
)
CREATE TABLE phoenix.dx
(
recid integer NOT NULL DEFAULT nextval('dx_recid_seq'::regclass),
disease_recid integer NOT NULL,
patient_recid integer NOT NULL,
CONSTRAINT pk_dx_recid PRIMARY KEY (recid),
CONSTRAINT dx_unique UNIQUE (tposted, patient_recid, disease_recid)
,
CONSTRAINT dx_disease_recid_fkey FOREIGN KEY (disease_recid)
REFERENCES phoenix.disease (recid) MATCH SIMPLE
ON UPDATE CASCADE
ON DELETE RESTRICT,
CONSTRAINT dx_patients FOREIGN KEY (patient_recid)
REFERENCES phoenix.patients (recid) MATCH SIMPLE
ON UPDATE CASCADE
ON DELETE RESTRICT
)
(Columns not involved in this question have been removed. :) )
There are many other children tables of disease with the same basic dependency on the disease table. Note that the primary key of the disease table is a foreign key to the dx table and that the dx table uses this foreign key in a unique constraint. Also note that the dx table is just one table of a long chain of table references. (That is the dx table also has its primary key referenced by other tables).
The problem: I wish to "edit" the contents of the parent disease record. By "edit", I mean:
change the data in the description column.
if the result of the change causes a duplication in the disease table, then one of the "duplicated" records will need to be deleted.
Herein lies my problem. There are many different tables that use the primary key of the disease table in their own unique constraint. If those tables ALSO have a foreign key reference to the duplicated record (in disease), then cascading the delete to those tables would be appropriate -- i.e., no duplication of records will occur.
However, if the child table does NOT have a reference to the "correct" record in the parent disease table, then simply deleting the record (by cascade) will result in loss of information.
Example:
Disease Table:
record 1: ID = 1 description = "ABC"
record 2: ID = 2 description = "DEF"
Dx Table:
record 5: ID = 5 refers to ID=1 of Disease Table.
Editing of record 1 in Disease table results in description becoming "DEF"
Disease Table:
record 1: ID = 1 "ABC" --> "DEF"
I have tried deferring the primary key of the disease table so as to allow the "correct" ID to be "cascaded" to the child tables. This causes the following errors:
A foreign key can not be dependent on a deferred column. "cannot use a deferrable unique constraint for referenced table "disease"
additionally, the parent table (disease) has no way of knowing ahead of time if its children already have a reference to the "correct" record so allowing deletion, or if the child needs to change its own column data to reflect the new "correct" id.
So, how can I allow a change in the parent table (disease) and notify the child tables to change their column values -- and delete within them selves should a duplicate record arise?
Lastly, I do not know today what future tables I will need. So I cannot "precode" into the parent table who its children are or will be.
Thank you for any help with this.

How to ensure that two columns in different tables have the same values

What T-SQL DDL is required to create a constraint that ensures that the values in a column in one table are the same as the values in a column in a different table?
I want to do this without using a PK-FK relationship.
The T-SQL DDL at the end of this post is an example of the generic problem that I'm trying to solve.
In this example, I want to know how to add an equality constraint between the two tables that ensures that the set of values in the column:
"PersonMayDriveCar.personName"
is always equal to the set of values in the column
"DriverLicense.personName"
CREATE SCHEMA "Equality Constraint"
GO
CREATE TABLE "Equality Constraint".PersonMayDriveCar
(
carVin nchar(4000) NOT NULL,
personName nchar(70) NOT NULL,
CONSTRAINT PersonMayDriveCar_PK PRIMARY KEY(personName, carVin)
)
GO
CREATE TABLE "Equality Constraint".DriverLicense
(
driverLicenseNr int NOT NULL,
personName nchar(70) NOT NULL,
CONSTRAINT DriverLicense_PK PRIMARY KEY(driverLicenseNr),
CONSTRAINT DriverLicense_UC UNIQUE(personName)
)
GO
I see that you want to maintain referential integrity between the two tables without using a foreign key.
Based on my past experience, I solved such an issue using a trigger.
So you can create a trigger on the DriverLicense table which ensures that any insert or update into the DriverLicense table will be rolled back if the inserted driverLicenseNr doesn't exist in the PersonMayDriveCar table.
You can go through this for a full example:
https://www.mssqltips.com/sqlservertip/4242/sql-server-referential-integrity-without-foreign-keys/
Adhere to convention:
Use an FK. It’s that simple.
Don’t link these table together with an FK, because they are both child tables of ...
Create a person table, which is the parent of the other two tables
Try this:
Person
- id (PK)
- name
- other columns
PersonMayDriveCar
- person_id (FK to person)
- other columns
DriverLicense
- person_id (FK to person)
- other columns

Entity framework - referencing one of composite primary key columns as a foreign key

In table A I've got a composite of 3 columns as a primary key. I want to have only one of these three columns as a foreign key in table B, just to make sure that the value that I insert into table B's column exists in table A.
Currently from what I've read it looks like in Entity Framework I have to add all three columns of composite PK, which is not really what I need. The latest answer that I've found was of 2015, maybe since then something changed?
I know that I can add a manual check on each insert/update call, but I don't want to do that, maybe there is more elegant way.

Is this kind of DB relation design favourable and correct? Should it be converted to a no-sql solution?

First of all, I did my research but being rather a newbie, I am not that well acquainted with words so might have failed in founding the correct ones. I beg your pardon in case of a possible duplicate.
Question #1:
I have a table consisting of ID [PK] and LABEL [Varchar 128]. Each record (row) here is unique. What I want is, to define relations between these LABELS.
Requisite:
There will be an n amount of groups, each group containing one or more of these LABELS. In each group, each LABEL can either exist or not exist (meaning a group does not have 2x of same LABEL).
How should I define this relation?
I thought of creating another table with ID [PK] - Group ID [randomly assigned unique key] - LABEL_ID [ID of Labels table pointing to a single Label]
Is this correct and favourable? If a group has 10 LABELS then there will be 10 records with unique ID, same uniquely assigned Group ID and LABEL_ID pointing to LABELS table.
Question #2:
Should I let go of the Relational solution (as described above) and opt for a NoSQL solution? Where Each group is stored on it's own as a single entry into the database with an ID [PK] - Data [Containing either labels or IDs of labels pointing to the Label table]?
If NoSQL is the way to go, how should I store this data?
a) Should I have ID - Data (containing Labels)?
b) ID - Data (containing IDs of Labels)?
Question #3:
If NoSQL solution here is the best way, which NoSQL database should I choose for this use case?
Thank you.
There's no real need for an ID column in this GroupLabels table:
CREATE TABLE GroupLabels (
GroupID int not null,
LabelID int not null,
constraint PK_GroupLabels PRIMARY KEY (GroupID,LabelID),
constraint FK_GroupLabels_Groups FOREIGN KEY (GroupID) references Groups,
constraint FK_GroupLabels_Labels FOREIGN KEY (LabelID) references Labels
)
By doing the above, we've automatically achieved a constraint - that the same label can't be added to the same group more than once.
With the above, I'd say it's a reasonably common SQL solution.
There is too little information here to make recommendations on the question of "to SQL or not to SQL".
However, the relational approach would be as you describe, I think.
CREATE TABLE Group
(
GroupId int PRIMARY KEY
)
CREATE TABLE GroupLabel
(
GroupId int FOREIGN KEY REFERENCES Group,
LabelId int FOREIGN KEY REFERENCES Label,
UNIQUE (GroupId, LabelId)
)
CREATE TABLE Label
(
LabelId int PRIMARY KEY,
Value varchar(100) UNIQUE
)
Here, every label is unique, Many labels may be in each group and each label may be in many groups but each label can only be in each group once.
As #Damien_The_Unbeliever indicates, the Group table can be omitted if you don't need to store any additional attributes about each group by making the GroupId column on the GroupLabels table solely unique.
You might need to change the syntax slightly for whatever RDBMS you're using.