POSTGRESQL Effective way to split table performance-wise - sql

I have a user table in postgress.
Each user can have from 0 to many websites.
I know that it will be waste of memory to bring the user-websites everytime I get the user object from the database and of course I cant know how many websites the user will have.
I could have a table called websites but then I think this could happen again with other sorts of lists that I want to add under the user profile.
What is the best solution for this problem?
Note: Best meaning a solution that will not affect the performance of the website.
FYI : the website will be running on ruby on rails 3

You can have something like this:
create table users (
user_id serial primary key,
username text not null unique
);
create table datatypes (
datatype_id serial primary key,
datatype text not null unique
);
create table data (
user_id int not null references users(user_id),
datatype_id int not null references datatypes(datatype_id),
data text not null
);
insert into datatypes (datatype)
values ('website','interest','contact_number');
Then add a website address 'example.com' to user 'testuser':
insert into data (user_id, datatype_id, data)
select user_id, datatype_id, 'example.com'::text as data
from users, datatypes
where username='testuser' and datatype='website';

Related

Foreign key to another table

I'm creating a database for storing user data and repeated data for each user (for example if it was for running it might have distance, time, date, etc. for each run). The users are all in one table
CREATE TABLE users ( name varchar(30), id int primary key );
Each user will submit data that needs to be put into a different table (schema doesn't matter here). I could either make this set of data into one big table wiht all the submitted data plus a key to the user
CREATE TABLE data ( ..., user_id int REFERENCES users );
or my prefered way which would be have one table per user and have each entry in the users table somehow refence the whole table for that user. I'm not quite sure how you'd do this and can't find any way to do so so far.
Any help is very appreciated thanks.
It's ok to use one table for all users data, just add index on field user_id:
CREATE TABLE users ( name varchar(30), id int primary key );
CREATE TABLE data (
--- ...,
user_id int REFERENCES users (id)
);
CREATE INDEX data_user_id_ix ON data(user_id);
https://sqlize.online/sql/psql11/44a7c7e8145db1e0583f8c91025da72f/

How do I ensure that a referencing table also has data

My Postgres database has the following schema where the the user can store multi profile images.
CREATE TABLE users(
id INT GENERATE AS ALWAYS PRIMARY KEY,
name VARCHAR(50)
);
CREATE TABLE images(
id INT GENERATE AS ALWAYS PRIMARY KEY,
url VARCHAR(50)
);
CREATE TABLE user_images(
user_id INT REFERENCES users(id),
image_id INT REFERENCES images(id)
);
How do I ensure that when I insert a user object, I also insert at least one user image?
You cannot do so very easily . . . and I wouldn't encourage you to enforce this. Why? The problem is a "chick and egg" problem. You cannot insert a row into users because there is no image. You cannot insert a row into user_images because there is no user_id.
Although you can handle this situation with transactions or delayed constraint checking, that covers only half the issue -- because you have to prevent deletion of the last image.
Here are two alternative.
First, you can simply add a main_image_id to the users table and insist that it be NOT NULL. Voila! At least one image is required.
Second, you can use a trigger to maintain a count of images in users. Then treat rows with no images as "deleted" so they are never seen.
When you insert a data into a table database can return a id from row which was inserted. So, if id > 0 the row has been inserted. But first, add column id (bigserial, auto increment, unique) to all tables.
INSERT INTO user_images VALUES (...) RETURNING id;

PostgreSQL - Where to store owners data?

It is ok to store the owners (the ones who access the main project dashboards) in the same database schema where normal users are stored? If so, how it is best: in the same "users" table with a flag "role" or in an independent table "owners".
CREATE TABLE users (
id BIGSERIAL NOT NULL PRIMARY KEY,
email TEXT NOT NULL UNIQUE,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE owners (
id BIGSERIAL NOT NULL PRIMARY KEY,
email TEXT NOT NULL UNIQUE,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
or
CREATE TABLE users (
id BIGSERIAL NOT NULL PRIMARY KEY,
email TEXT NOT NULL UNIQUE,
role INT NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
I recommend to store the users in one table.
Here is why:
One table storing very similar kind of data
It's easier to upgrade a user to a superuser
Maintenance
Follow up sql statements using users will be less complex
I would say that having only one table is easier to maintain and simplifies the logic of the app. The "role" field allows future expansion in case you'd like to add new role types. Security-wise, having one or two tables should be very similar. But it depends of how you access the table, permissions, ...
I'm not sure this answer is very helpful, but we would need a bit more of context to be able to help you further...

Add serial value across multiple tables

I have the following two tables in my Postgres database:
CREATE TABLE User (
Id serial UNIQUE NOT NULL,
Login varchar(80) UNIQUE NOT NULL,
PRIMARY KEY (Id,Login)
);
CREATE TABLE UserData (
Id serial PRIMARY KEY REFERENCES Users (Id),
Password varchar(255) NOT NULL
);
Say, I add a new user with INSERT INTO Users(Id, Login) VALUES(DEFAULT, 'John') and also want to add VALUES(id, 'john1980') in UserData where id is John's new id.
How do I get that id? Running a query for something just freshly created seems superfluous. I have multiple such situations across the database. Maybe my design is flawed in general?
(I'm obviously not storing passwords like that.)
1) Fix your design
CREATE TABLE usr (
usr_id serial PRIMARY KEY,
,login text UNIQUE NOT NULL
);
CREATE TABLE userdata (
usr_id int PRIMARY KEY REFERENCES usr
,password text NOT NULL
);
Start by reading the manual about identifiers and key words.
user is a reserved word. Never use it as identifier.
Use descriptive identifiers. id is useless.
Avoid mixed case identifiers.
serial is meant for a unique column that can be pk on its own. No need for a multicolumn pk.
The referencing column userdata.usr_id cannot be a serial, too. Use a plain integer.
I am just using text instead of varchar(n), that's optional. More here.
You might consider to merge the two tables into one ...
2) Query to INSERT in both
Key is the RETURNING clause available for INSERT, UPDATE, DELETE, to return values from the current row immediately.
Best use in a data-modifying CTE:
WITH ins1 AS (
INSERT INTO usr(login)
VALUES ('John') -- just omit default columns
RETURNING usr_id -- return automatically generated usr_id
)
INSERT INTO userdata (usr_id, password )
SELECT i.usr_id, 'john1980'
FROM ins1 i;
You can consider using a trigger. The Id column of the newly inserted row can be accessed by the name NEW.Id.
References:
CREATE TRIGGER documentation on PostgreSQL Manual
Trigger Procedures

Store array of items in SQL table

I know this has probably been asked a million times but I can't find anything definite for me. I'm making a website involving users who can build a list of items. I'm wondering what would be the best way for store their items in an SQL table?
I'm thinking will I need to make a seperate table for each user since there I can't see any way to store an array. I think this would be inefficient however.
Depending on what an "item" is, there seem to be two possible solutions:
a one-to-many relationship between users and items
a many-to-many relationship between users and items
If a single item (such as a "book") can be "assigned" to more than one user, it's 2). If each item is unique and can only belong to a single user it's 1).
one-to-many relationship
create table users
(
user_id integer primary key not null,
username varchar(100) not null
);
create table items
(
item_id integer primary key not null,
user_id integer not null references users(user_id),
item_name varchar(100) not null
);
many-to-many relationship:
create table users
(
user_id integer primary key not null,
username varchar(100) not null
);
create table items
(
item_id integer primary key not null,
item_name varchar(100) not null
);
create table user_items
(
user_id integer not null references users(user_id),
item_id integer not null references items(item_id)
);
Because of your extremely vague description, this is the best I can think of.
There is no need to use an array or something similar. It seems you are new to database modelling, so you should read up about normalisation. Each time you think about "arrays" you are probably thinking about "tables" (or relations).
Edit (just saw you mentioned MySQL): the above SQL will not create a foreign key constraint in MySQL (even though it will run without an error) due to MySQL's stupid "I'm not telling you if I can't do something" attitude. You need to define the foreign keys separately.
A separate table for each user\account would be best. This will limit the size of the necessary tables and allow for faster searching. When you present data you are usually displaying data for that current user/account. When you have to search through the table to find the relative information. The application will start to slow down the larger the dependent table grows. Write the application as if it will be used to the fullest extent of SQL. This will limit the need for redesign in the future if the website becomes popular.