postgreSQL/PostGIS database: Table inheritance - sql

I am relatively new to postgreSQL and database design, and am currently puzzling with the method of INHERITANCE and whether it is relevant or appropriate to apply it to some of the tables in the postgreSQL/
PostGIS database I am currently creating. I would greatly appreciate any advice.
The three table scenarios I am unsure about are presented below:
1)
The first case is an ecology table that holds information about flora and fauna (see below). All the attributes, except the resourceGroup and name attributes, takes the same types of input values
regardless of whether the record refers to flora or fauna. The values that goes in the resourceGroup and name, refer to values from different lookup tables that are specific to either fauna or flora.
CREATE TABLE "ecology" (
"ecology_id" serial NOT NULL,
"resourceType" varchar(5) # ie. flora, fauna
"resourceUse" varchar(20) NOT NULL,
"resourceGroup" varchar(20) NOT NULL # takes input from different lookup tables depending if it is flora (ie. roots, seeds...) or fauna (ie. mammal, reptile ..)
"native" boolean NOT NULL,
"name" varchar(30) NOT NULL, # ie species name
"englishName" varchar(30) NOT NULL,
"NTStatus" varchar(20) NOT NULL,
"description" varchar(255) NOT NULL,
CONSTRAINT "ecology_pk" PRIMARY KEY ("ecology_id")
);
Question: Is inheritance the answer here, ie.
CREATE TABLE "fauna" (
"faunaGroup" varchar(5)
"name" varchar(30) NOT NULL,
"englishName" varchar(30) NOT NULL) INHERITS(ecology);
..and similar for flora, and then remove those attributes from the ecology table. Or is there a way to specify a constraint on the ecology table that when resourceType is
set to ie. flora, then the input values in the resourceGroup and name fields are restricted to the lookup tables that refer to respectively flora name and flora group, and vise versa?
2)
The other potential Inheritance table scenario refers to the table documentation. This table stores information on and urls to different types of documentation methods, ie. reports
AV, images, forms ect. The problem here is that some of the documentation types, such as reports and AV, share a many-to-many relationship with another main entity, while ie.
images and forms are one-to-many. Besides this they share the same type of attribute information
Question: is inheritance the way to go to account for the different types of relationships, or should I just group them all together in one table (documentation) and treat all as a many-to-many relationship?
3)
This table scenario concerns certain tables that stores culturally sensitive information. There are essentially three access restrictions to some of the attributes in these
tables: public, men only, and women only.
Question: Again could inheritance be the way forward here. Ie. creating two child tables for respectively men and women, that inherits all or some of the attributes from the parent
table. You could then ecrypt respectively the men and women child tables? Or alternatively, would a simpler or better solution be to account for
the gender and public restricted access at the schema level. So ie. creating respectively a men and women schema and group roles for each work type (ie ranger work, planning, ect ), as well as a schema that
stores information that is open to the public.
Thanks in advance.
Best
Ric.

ecology / fauna / flora:
I think that inheritance is a possible way to model that. You seem to have got the table definitions wrong though: the ecology table should have the attributes that both fauna and flora have, and you only specify those rows in the inheritance children that are added.
documentation:
That could also work with inheritance. Since all inheritance children are tables in their own right, they can have foreign key constraints with other tables.
information visible to men or women:
That cannot be modeled well with inheritance. If you want to restrict visibility to table rows based on the database role, look into Row Level Security.

Related

Are there any naming conventions for self-referential, many-to-many relationship tables?

Let's say we have a table called Users for a fictional social media platform. In addition to regular users, some more experienced users are asked to be "greeters" where they are assigned other, newer users to greet and encourage. Because greeters aren't always available, multiple greeters are assigned to each new user and each greeter is assigned multiple new users.
Thus we have a self-referential, many-to-many user relationship we need a link table for.
Are there any conventions for this case?
Here are some ideas based on conventions I've found for standard many-to-many tables:
user_user: doesn't tell us much and there might be more than one relationship like this
Greeters: (i.e. use a new name that describes the relationship) pretty good, but it doesn't communicate that it's a link table or related to Users
greeter_user: (use a different name for one part of the relationship)
What do you use or what would you recommend?
In the entity–relationship model this would be modeled as user encourages user.
This could be translated into the tables Users and Encouragements.
CREATE TABLE Users (
username VARCHAR(32) PRIMARY KEY,
...
);
CREATE TABLE Encouragements (
greeterName VARCHAR(32),
newbieName VARCHAR(32),
PRIMARY KEY (greeterName, newbieName),
FOREIGN KEY (greeterName) REFERENCES Users(username),
FOREIGN KEY (newbieName) REFERENCES Users(username)
);
https://en.wikipedia.org/wiki/Entity%E2%80%93relationship_model

What are the benefits of using separate role-bridge table over all-in-one table?

I have a bridge table book_person between tables book and person to provide many-to-many relation. In this table I also have role-definitions, to set which roles (author, editor, illustrator, translator etc.) a person has on particular book. Now I consider to split roles to separate role tables (like book_author, book_translator etc). But I am in doubt, is it good idea or not? For pros, it makes DB more clean and one simple benefit I see that DBIC schema loader detects such simple bridge-tables and creates many-to-many accessors to me. For cons I see that aggregating functions for roles will need more joinings.
What are the benefits of using separate role-bridgetable over all-in-one role-bridgetable? And what are shortcomings? I am trying to upgrade my apps using ORM (DBIx::Class), but not knowing it well yet, so considerations towards it are also really welcome.
create table book (
id_book integer primary key,
name_book text,
author_book
);
create table person (
id_person integer primary key,
name_person text
);
create table book_person (
id_person integer,
id_book integer,
role_person text,
primary key (id_person, id_book)
);
I think using role in book_person table is a good choice if one person has ONLY one role because:
There are just a few roles which you use.
To wouldn't to litter your DB when you could do it if created some more tables like book_author, book_translator.
You don't need to use as you said many joinings.
Roles in your case just an attribute and if you don't keep some extra info about roles capabilities you shouldn't create one more table for keeping binding role-person. You already keep it in book_person.
You need to create another table for role if you have:
One person has more than one role.
You keep some extra info about role as I said above.
I guess that's all.
Given that a person can have multiple roles for a book, I would create a separate table (say book_person_role) with person-id/book-id as foreign key and a role-id. Thus you get a one-to-many relation from book_person to book_person_role. I wouldn't create a table per role; that would mean changing the schema when a role is added/deleted/changed.

SQL attributes depending on type

Let's say I have an entity CLIENT, which can be either PERSON or ORGANIZATION. Depending on which type it is, I have to choose attributes (address, name for organization, date_of_birth,first_name,last_name for person). I have created all three entities, but how can I make the attributes type-dependent?
Seen Database design: objects with different attributes, didn't help...
One typical choice is a 1:1 extension table:
create table client (id int primary key);
create table person (id int foreign key references client(id), ...columns...);
create table organization (id int foreign key references client(id), ...columns...);
However, my preferred choice is to include all columns in the client table. You can have a column for type that is either person or organization. Columns that are not relevant for the row's type can be null. Your queries will be much simpler that way.
Either you use 3 tables or you use 1 table and leave the not needed columns null. Which design is superior depends on the use case. Using only 1 table gives simpler queries but requires to change the table for each new subclass. Using multiple tables allows to add more types easily but gives more complicated queries. In doubt I would start with only 1 table but your mileage may vary.

Should one-to-one relationships ever be split into two tables?

I have one table [Users] and another table [Administrators] linked 1:0..1 . Is it best practice to merge these tables? I have read a lot of answers on SO stating splitting tables is only necessary for one-to-many relationships.
My reasoning for separating them is so I can reference administrators with AdministratorId rather than the general UserId. In other tables I have fields which should only ever contain an administrator so it acts as a referential check.
There is a rule of thumb that states a table either models an entity/class or the relationship between entities/classes but not both. However, it is only a rule of thumb, never say never!
SQL generally has a problem with dedicated 1:1 relationship tables because the only inter-table constraints commonly found are foreign keys. However, a FK does not require that a value exists in the referencing table. This makes the relationship 1:0..1 ("one-to-zero-or-one"), which is usually acceptable.
Strict 1:1 requires a workaround. Because SQL lacks multiple assignment, the workaround usually involves resorting to procedural code e.g. two deferrable 'bi-directional' FKs; triggers; forcing updates via CRUD stored procs; etc.
In contrast, modelling a 1:1 relationship in the same table is easy: declare both columns as NOT NULL!
I think the best option is to have two tables for the two different entities, Users and Administrators, possibly with the same Primary Key.
CREATE TABLE User
( UserId int
, ... other data --- data for all users
, PRIMARY KEY (UserId)
) ;
CREATE TABLE Administrator
( AdministratorId int
, ... other data --- data for administrators only
, PRIMARY KEY (AdministratorId)
, FOREIGN KEY AdministratorId
REFERENCES User(UserId)
) ;
This way, as you mention, other tables can reference the AdministratorId:
CREATE TABLE OtherTable
( OtherTableId int
, AdministratorId int
, ... other data
, ...
, FOREIGN KEY AdministratorId
REFERENCES Administrator(AdministratorId)
) ;
Benefits:
referential integrity is trivially implemented.
the relevant data (for Users and Admins) can be stored in the relevant tables so you have less columns in the tables and fewer NULL data.
any query that needs a JOIN to Administrator table will have to look up only a few rows, compared to the (possibly huge) number of rows of the User table. If you have only one table, you'll end up with code like:
WHERE User.admin = True
which may not be easily optimized.
There are several reasons why you might want to keep them separate. One is if the records in one table represent a subset of the records in the other. This patterns is called sub-classing, and is clearly the case in your situation.
This is wise even if the fields (the data) you need to store about admins are not different from the data you need to store about all users. Another reason is if the the usage patterns for a few columns is very different (greater frequency of access) from the usage patterns for the rest of the columns.
Let me stick to the title of your Question first.
Yes, One to one relationship can be split into different tables when you need the following:
Modularity
Security / Data Abstraction
I guess the modularity bit is quite clear.
The Security bit is obvious too since you will require access to both the tables to fetch the whole picture of data.
For Example, Let's suppose you have the below as the scenario:
Wherein, Customer has a dummy id for passport and Passport table doesn't have corresponding customer info.
Hence, the person with access to both tables can only map the passport id with the customer and see the whole picture.
It is common to have tables in a one-to-one relationship. First if they are separate entities that will need to be queried separately or if they are subclasses (as in your case) then the separate table makes sense. Also if the primary table is getting too large for the maximum record size it makes sense to have an additional table in a one-to-one relationship. Finally you have the case where the relationship is 1-1 now, but has the potential to be 1-many in the future such as when you only have one phone number now but wil probably need to store more later. In this case it will be less work to go ahead and make it a separate table.
The crtical piece to setting up a 1-1 relationship is to enforce it as 1-1. The easiest way to do this is to make the FK field also the PK field in the second table.

Creating an SQL Schema (postgresql)

I'm having problems creating a schema for a PostgreSQL project.
It's for a social networking site, if there is a profile, and each profile comes in three varieties: generic, education, and employment profiles, therefore each profile requires different attributes… how do we do this all in the one table?
create type ProfileTypeValue as enum
('generic', 'education', 'employment');
create Profiles (
id integer
type ProfileTypeValue
....?
primary key (id)
);
because for instance if it's an education profile, then we need to have institution name etc, or if it's an employment profile, then we need to have an employer name attribute, etc.
Is it best to just have 3 different tables, 1 for each profile Type, dont know if thats possible… but I feel like I need to have an if statement saying if it's profile, include these attributes, or if its a profile, include these attributes, etc.
Here's a couple of options
All in the same table
Common profile attributes in one table, profile type specific in their own tables with foreign key references to the common profile table
Inheritance
Key-Value store
All in the same table
In this option all the fields are always present whatever type the profile is. This is too easy to do the first time around as you only have to list all the columns. However, this is really bad design that will make your life harder in the long run, because the maintainability and extendability is poor. You should read up on database normal forms etc. Don't do this.
Master profile table and profile type dependent details on their own tables
In this option you will create a table for all profiles. This will include all the common attributes. This table will make sure the identifiers are all in the same namespace and each profile has a unique id. For each profile type you'll create a new table that has a foreign key reference to the master profile table. You can then select all employment profiles using an inner join on the employment profile table and the master profile table. This design allows you to create constraints for each profile type. Furthermore, this design lets you have profiles that are both employment and education profiles. You should probably do this.
Inheritance
Postges provides a facility for table inheritance. You can use this by creating a base table for all profile types and then creating child tables for each profile type. Each profile type then inherits all the attributes defined in the parent table. With inheritance you can select all profiles using the parent table and all employment profiles using the employment profile table. If generic profiles use only common attributes, they can be stored to the parent table.
The main disadvantage of inheritance in postgres is that parent table and the child tables do not share the same namespace. You cannot create a unique constraint that spans all the tables. This means that you have to make sure that the identifiers are globally unique some other way e.g. keeping a separate table for the profile identifiers.
You should think if the disadvantages of inheritance matter in you situation. However, this is the sensible way of doing separate tables for all profile types if you are using postgres as you don't have to duplicate the definitions of the common attributes.
Key-value store
You could also create a table for common profile attributes and keep the rest of the attribues in (profile, attribute, value)-tuples. By doing this, you'd discard the benefits of a RDBMS and you'd have to implement all the logic in you program. Don't do this.
PostgreSQL supports table level inheritance. You can make a Profile table as the parent table with common attributes and then separate child tables for education and employment with only attributes specific to those categories
Check out the PostgreSQL documentation here.