Best way to store company and employee data - data-management

I want to create an application where shop owner can register their shop and can store their customer details. My question is what will be the best way to store shop and their corresponding customer information.
I can create store and customer table and can have foreign key mapping. But is there any alternative and more secure way of doing this? Here security is primary concern. One shop owner should not be able to see other shop owner details.

You can create a third table with the name of shop_customers where you have both the ids of shop and customer as foreign key mapping.
Create table shops(
shop_id integer,
primary key (shop_id)
);
Create table customers(
customer_id integer,
primary key (customer_id)
);
CREATE TABLE shop_customers(
shop_id integer,
customer_id integer,
Primary Key (shop_id,customer_id),
Foreign Key (shop_id) REFERENCES shops(shop_id),
Foreign Key (customer_id) REFERENCES customers(customer_id)
);

Related

How do I define the main/primary record of a one-to-many relationship?

I'm making a service with an account table and a profile table. An account can own multiple profiles, but accounts always have a primary profile. This would initially be the first profile created alongside the account upon registration, but the user could add profiles and set one as the primary profile later.
I've tried the following setup:
create table account (
id uuid primary key,
email text unique,
phone text unique,
created_at timestamptz,
primary_profile_id uuid references profile on delete restrict,
);
create table profile (
id uuid primary key,
account_id uuid references account on delete cascade,
username text unique,
about text,
created_at timestamptz
);
This doesn't work because:
You can't even run that to create the tables since they both rely on the other existing beforehand. A workaround would be to create the account table without primary_profile_id and alter the table to add that column after the creation of the profile table, however...
Even if you can create the tables, you can't add records to them because you would need the other to exist first to reference it. It is technically possible if you leave primary_profile_id as NULL and change it after the profile has been created, but the columns aren't supposed to be nullable, so it's not really ideal.
Is there any good solution to this? I've also considered having a primary boolean on the profile table, but then there's nothing on the database side preventing that being true for multiple profiles under a single account.
Thanks for any help :)
Your idea of enforcing your requirement with a foreign key is good.
Creating the tables is no problem; you can simply run
create table account (
id uuid primary key,
email text unique,
phone text unique,
created_at timestamptz,
primary_profile_id uuid,
);
create table profile (
id uuid primary key,
account_id uuid references account on delete cascade,
username text unique,
about text,
created_at timestamptz
);
ALTER TABLE account
ADD FOREIGN KEY (primary_profile_id) REFERENCES profile
DEFERRABLE INITIALLY DEFERRED;
A deferred foreign key constraint like that is not checked right when a row is inserted, but at the end of the transaction. So you can add account first and then the matching profile, as long as you Insert both in the same transaction.
I recommend using NOT NULL in your column definitions wherever possible.

Using PostgreSQL composite foreign keys to prevent cross organization associations

I am considering using composite foreign keys in PostgreSQL to ensure that rows which belongs to different organizations cannot be associated with each other. Is this a good idea, or will I live to regret it?
For example, in a system which has addresses and shipments, both of which belong directly to an organization, if a shipment has an address, with composite foreign key constraints I could limit that the address must belong to the same organization.
So simplified, I would have something like this:
CREATE TABLE organizations (
id SERIAL PRIMARY KEY,
name TEXT
);
CREATE TABLE addresses (
id SERIAL PRIMARY KEY,
organization_id INTEGER NOT NULL,
FOREIGN KEY (organization_id) REFERENCES organizations (id),
UNIQUE (id, organization_id)
);
CREATE TABLE shipments (
id SERIAL PRIMARY KEY,
organization_id INTEGER NOT NULL,
address_id INTEGER NOT NULL,
FOREIGN KEY (organization_id) REFERENCES organizations (id),
FOREIGN KEY (address_id, organization_id) REFERENCES addresses (id, organization_id),
UNIQUE (id, organization_id)
);
On the surface this seems good, since it ensures better data integrity, but possibly insertion could become slow, and maybe there are other issues I am not foreseeing?

Is there a way to create a unique constraint for 2 tables?

Currently, I have the following 3 tables.
CREATE TABLE customer (
id SERIAL PRIMARY KEY
);
CREATE TABLE google_subscription (
fk_customer_id INTEGER NOT NULL UNIQUE,
CONSTRAINT fk_customer_id_constraint
FOREIGN KEY(fk_customer_id)
REFERENCES customer(id)
ON DELETE RESTRICT
);
CREATE TABLE apple_subscription (
fk_customer_id INTEGER NOT NULL UNIQUE,
CONSTRAINT fk_customer_id_constraint
FOREIGN KEY(fk_customer_id)
REFERENCES customer(id)
ON DELETE RESTRICT
);
google_subscription is having fk_customer_id referencing to customer table id.
apple_subscription is having fk_customer_id referencing to customer table id.
I was wondering, is it ever possible to create a constraint, such that customer table id, will only be found in either google_subscription or apple_subscription, but NOT both?
No that is not possible. A constraint cannot span across multiple tables. Instead you can have another table subscriptions where you can have a unique index on customer(id). Or you can have another column in customer table which will hold only 1 subscription at a time.

Validating foreign key through tables in postgres

I'm trying to validate a foreign key constraint on my table through another table, but I'm not sure how to go about it, or even the correct nomenclature to google.
I have a User table:
create table User ( id uuid primary key );
Each user can have multiple stores:
create table Store(
id uuid primary key,
user_id uuid foreign key store_user_id_fk references User (id)
);
and each user+store can have products
create table Product(
id uuid primary key,
user_id uuid foreign key product_user_id_fk references User (id),
store_id uuid foreign key product_sotre_id_fk references Store (id),
)
My question is: how can I write a constraint on Product such that any (user_id,store_id) combination also must have a valid entry in the Store table? The case I'm trying to prevent is an entry being added to Product where the store does not also belong to the user.
Is there some way of adding:
CHECK ( store_id == Store.id and user_id == Store.user_id )
To the product table?
Unless I'm misunderstanding your question, I'm pretty sure it would just be:
create table Product(
id uuid primary key,
user_id uuid foreign key product_user_id_fk references User (id),
store_id uuid foreign key product_sotre_id_fk references Store (id),
FOREIGN KEY (user_id, store_id) REFERENCES store(user_id, id)
);
This would indicate that the primary key of store should be (id, user_id) not just id or, at at minimum, it should have a UNIQUE constraint on (id, user_id).

How to use constraints on ranges with a junction table?

Based on the documentation it's pretty straightforward how to prevent any overlapping reservations in the table at the same time.
CREATE EXTENSION btree_gist;
CREATE TABLE room_reservation (
room text,
during tsrange,
EXCLUDE USING GIST (room WITH =, during WITH &&)
);
However, when you have multiple resources that can be reserved by users, what is the best approach to check for overlappings? You can see below that I want to have users reserve multiple resources. That's why I'm using the junction table Resources_Reservations. Is there any way I can use EXCLUDE in order to check that no resources are reserved at the same time?
CREATE TABLE Users(
id serial primary key,
name text
);
CREATE TABLE Resources(
id serial primary key,
name text
);
CREATE TABLE Reservations(
id serial primary key,
duration tstzrange,
user_id serial,
FOREIGN KEY (user_id) REFERENCES Users(id)
);
CREATE TABLE Resources_Reservations(
resource_id serial,
reservation_id serial,
FOREIGN KEY (resource_id) REFERENCES Resources(id),
FOREIGN KEY (reservation_id) REFERENCES Reservations(id),
PRIMARY KEY (resource_id, reservation_id)
);
I think what you want is doable with a slight model change.
But first let's correct a misconception. You have foreign key columns (user_id, resource_id, etc) defined as SERIAL. This is incorrect, they should be INTEGER. This is because SERIAL is not actually a data type. It is a psuedo-data type that is actually a shortcut for: creating a sequence, creating a column of type integer, and defining the sequence created as the default value. With that out of the way.
I think your Resources_Reservations is redundant. A reservation is by a user, but a reservation without something reserved would just be user information. Bring the resource_id into Reservation. Now a Reservation is by a user for a resource with a duration. Everything your current model contains but less complexity.
Assuming you don't have data that needs saving, then:
create table users(
id serial primary key,
name text
);
create table resources(
id serial primary key,
name text
);
create table reservations(
user_id integer
resource_id integer
duration tstzrange,
foreign key (user_id) references users(id)
foreign key (resource_id) references resources(id),
primary key (resource_id, user_id)
);
You should now be able to create your GIST exclusion.