0-to-many and 1- to-many relationship in PostgreSQL code - postgresql

From a PostgreSQL article on foreign keys constraints at https://www.postgresql.org/docs/current/ddl-constraints.html:
Say you have the product table :
CREATE TABLE products (
product_no integer PRIMARY KEY,
name text,
price numeric
);
Let's also assume you have a table storing orders of those products.
We want to ensure that the orders table only contains orders of
products that actually exist. So we define a foreign key constraint in
the orders table that references the products table:
CREATE TABLE orders (
order_id integer PRIMARY KEY,
product_no integer REFERENCES products (product_no),
quantity integer
);
Now it is impossible to create orders with non-NULL product_no entries
that do not appear in the products table.
Is this a 0-to-many relationship?
How can I set a one-to-many relationship?
By doing this?
CREATE TABLE orders (
order_id integer PRIMARY KEY,
product_no integer REFERENCES products (product_no) NOT NULL,
quantity integer
);
If that is the case, why does pgadmin4 generate this crow foot notation that denotes a zero-to-many when I configure the table as above?

this crow foot notation denotes a zero-to-many relationship.
No, it doesn't. Do not confuse the cardinality (which is directional) with the type of the relation. The circle denotes the lower bound of the orders cardinality (and may be omitted) - see "many" vs "zero or many"/"one or many" crowfoot notation? on Software Engineering.
Marking the product_no as NOT NULL only changes a zero-or-one-to-many relationship into an exactly-one-to-many relationship. Both are commonly referred to as "one-to-many" or "1:N" relationships. This would be expressed as either a dash-dash or dash-circle (or, amiguously, just dash) for the cardinality of products.
Now the circle (or dash) on the crow foot is just about the many side (orders) - is it …-to-zero-or-many or …-to-one-or-many? And in your schema, it is indeed a zero-or-many cardinality, since there can be any number of orders - including 0 - for a product.
Notice that an actual 0,1:1,N, 1:1..N or 1..N:1..N relationship (not …:0,N) is surprisingly hard to represent in SQL since you have a chicken-and-egg problem, see How to Create a real one-to-one relationship in SQL Server or 1:N relationship where N must be at least one entry).

Related

Postgres 2 different FKs in both directions

I've run into a situation where we have 2 tables that have 2 different foreign key relationships, one is 1-many, the other is 1-1, but they are in opposite directions. I'm wondering if postgres has a way to validate that the combination of the FKs must match, probably easier to show in an example.
table product {
id: PK
name ...etc
preferred_vendor_product: FK to vendor_product id
}
table vendor_product {
id: PK
product_id: FK to product (a product could have many vendor products)
price...etc
}
Is there a way to enforce that when I add a preferred vendor product to product, that not only the vendor_product must exist, but it's value in product_id must match the product in question.
Just have a third table preferred_vendor_product that just has a fkey pointing to vendor_product. That table presumably should have a primary key of (vendor_id, product_id).

PostgreSQL database: Get rid of redundant transitive relation (maybe 3NF is failed)

I'm creating a hybrid between "X-Com Enemy Unknown" and "The Sims". I maintain game state in a database--PostgreSQL--but my question is structural, not engine-specific.
As in X-Com, there are some bases in different locations, so I create a table named Base with ID autoincrement identity as primary key.
Every base has some facilities in its territory, so I create a table named Facility with a foreign key Facility.Base_ID, referring to Base.ID.
Every base has some landing crafts in its hangars, so I create a table named Craft with a foreign key Craft.Base_ID, referring to Base.ID.
Every base has some troopers in its barracks, so I create a table named Trooper with a foreign key Trooper.Base_ID, referring to Base.ID.
Just to this point, everything seems to be ok, doesn't it? However...
I want to have some sort of staff instruction. Like in the X-Com game, every trooper can be assigned to some craft for offense action, or can be unassigned. In addition, every trooper can be assigned to some facility (or can be unassigned) for defense action. So, I have to add nullable foreign keys Trooper.Craft_ID and Trooper.Facility_ID, referring to Craft.ID and Facility.ID respectively.
That database has a redundancy. If some trooper is assigned to a craft or to a facility (or both), it has two (or even three) relations to the base--one direct relation through its Base_ID and some indirect relations as Facility(Trooper.Facility_ID).Base_ID and Craft(Trooper.Craft_ID).Base_ID. Even if I get rid of Trooper.Base_ID (e.g. I can make both assignment mandatory and create a mock craft and a mock facility in every base), I can't get rid of both trooper-facility-base and trooper-craft-base relations.
In addition to this redundancy, there is a worse problem--in case of a mistake, some trooper can be assigned to a craft from one base and to a facility from another base, that's a really nasty situation. I can prohibit it in the application business logic tier, but it's still allowed by the database.
There can be some constraints to apply, but is there any structural modification to the schema that can get rid of the redundancy and potential inconsistency as a result of a good structure, not as a result of constraints?
CREATE TABLE base (
id int PRIMARY KEY
);
CREATE TABLE facility (
id int PRIMARY KEY,
base_id int REFERENCES base
);
CREATE TABLE craft (
id int PRIMARY KEY,
base_id int REFERENCES base
);
CREATE TABLE trooper (
id int PRIMARY KEY,
assigned_facility_id int REFERENCES facility,
assigned_craft_id int REFERENCES craft,
base_id int REFERENCES base
);
Now I want to get some sort of constraints on a trooper t so that
facilities.get(t.assigned_facility_id).base_id IS NULL OR EQUAL TO t.base_id
crafts.get(t.assigned_craft_id).base_id IS NULL OR EQUAL TO t.base_id
This hypothetical constraint has to be applied to table trooper, because it applies in boundaries of each trooper row separately. Constraints on one table have to check equality between fields of two other tables.
I would like to create a database schema where there is exactly one way, having a trooper.id, to find its referenced base.id. How do I normalise my schema?

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.

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

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.

Do i really need individual table for my three types of users?

If i have three type of users. Let's say seller, consumers, and sales persons. Should i make individual table for there details like name, email passwords and all other credentials etc with a role_type table or separate table for each of them. Which is the best approach for a large project considering all engineering principles for DBMS like normalization etc.
Also tell me Does it effect the performance of the app if i have lots of joins in tables to perform certain operations?
If the only thing that distinguishes those people is the role but all details are the same, then I would definitely go for a single table.
The question is however, can a single person have more than one role? If that is never the case, then add a role_type column to the person table. Depending on how fixed those roles are maybe use a lookup table and a foreign key, e.g.:
create table role_type
(
id integer primary key,
name varchar(20) not null unique
);
create table person
(
id integer primary key,
.... other attributes ...,
role_id integer not null references role_type
);
However, in my experience the restriction to exactly one role per person usually doesn't hold, so you would need a many-to-many relation ship
create table role_type
(
id integer primary key,
name varchar(20) not null unique
);
create table person
(
id integer primary key,
.... other attributes ...,
);
create table person_role
(
person_id integer not null references person,
role_id integer not null references role_type,
primary key (person_id, role_id)
);
It sounds like this is a case of trying to model inheritance in your relational database. Complex topic, discussed here and here.
It sounds like your "seller, consumer, sales person" will need lots of different attributes and relationships. A seller typically belongs to a department, has targets, is linked to sales. A consumer has purchase history, maybe a credit limit, etc.
If that's the case,I'd suggest "class table inheritance" might be the right solution.
That might look something like this.
create table user_account
(id int not null,
username varchar not null,
password varchar not null
....);
create table buyer
(id int not null,
user_account_id int not null(fk),
credit_limit float not null,
....);
create table seller
(id int not null,
user_account_id int not null(fk),
sales_target float,
....);
To answer your other question - relational databases are optimized for joining tables. Decades of research and development have gone into this area, and a well-designed database (with indexes on the columns you're joining on) will show no noticeable performance impact due to joins. From practical experience, queries with hundreds of millions of records and ten or more joins run very fast on modern hardware.