Auto increment column depending other columns value - sql

Hi I'm very new to Postgresql or SQL in general, so my terminology will probably be off.
I'm trying to add a version number column to my text text table.
name
type
desc
text_id
uuid
unique id
post_id
uuid
id of the collection of versions
version_nr
INT
version number of document
created_at
TIMESTAMP
when the document was edited
So basically when I create a new row I want to increment the version number, but I don't want all the rows to share the same "increment". I want all rows sharing the same post_id to have their own "increment".
This is what I've come up with this far:
CREATE TABLE text (
text_id uuid PRIMARY KEY DEFAULT UUID_GENERATE_V4(),
post_id uuid NOT NULL,
version_nr SERIAL -- <--- I DONT KNOW
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
)
I just don't understand how to solve the version_nr part.
Thanks!

Related

Full text search on many to many relationship

I have the following tables
The keywords table
CREATE TABLE trigger_keyword
(
id bigint NOT NULL,
keyword text NOT NULL,
CONSTRAINT trigger_keyword_id PRIMARY KEY (id)
)
This is the bridge table
CREATE TABLE trigger_keyword_trigger_message
(
trigger_keyword_id bigint NOT NULL,
trigger_message_id bigint NOT NULL,
CONSTRAINT trigger_keyword_trigger_message_trigger_keyword_id_fkey FOREIGN KEY (trigger_keyword_id)
REFERENCES public.trigger_keyword (id) MATCH SIMPLE
ON UPDATE CASCADE
ON DELETE NO ACTION,
CONSTRAINT trigger_keyword_trigger_message_trigger_message_id_fkey FOREIGN KEY (trigger_message_id)
REFERENCES public.trigger_message (id) MATCH SIMPLE
ON UPDATE CASCADE
ON DELETE NO ACTION
)
The message table
CREATE TABLE trigger_message
(
id bigint NOT NULL,
message text NOT NULL,
CONSTRAINT trigger_message_id PRIMARY KEY (id)
)
I have a list of strings outside of the PostgreSQL database, which I will run in a loop.
Let's assume we have the following keywords in the trigger_keyword table
The trigger_keyword table
id keyword
----------------------------------------
1 hi
2 hello
3 the weather
4 the climate
The trigger_message table
id message
-----------------------------------------
1 Hi how is your day?
2 Hello, have a wonderful day
3 Looks sunny today
4 Excellent, no rain today
5 looks like we'll have showers today
Let's say one of our strings is Hi Robot!, then the SQL query should return Hi how is your day? or Hello, have a wonderful day; it should pick one of them randomly. It should do the same if the string contained hello robot instead of hi robot since both hi and hello are in the keywords table.
And if the string contains tell me the weather then the SQL query should return Looks sunny today or Excellent, no rain today or looks like we'll have showers today randomly.
I assume I would have to use full text search for this?
It's my first time using a bridge table, do I manually insert the relations in the bridge table?
You should define a primary key constraint on the “bridge” table that contains both columns.
Full text search as indicated in this answer is one way to do this.
To randomly pick one result row, you can append the following to the query:
ORDER BY random() LIMIT 1
To insert into the tables, you could use a DEFAULT clause with a sequence in the definition of the id columns and use INSERT ... RETURNING to get the values for the bridge table.

Modelling Post and Flag relationship in SQL

I am modeling the data for my web I am building. I use Postgresql database.
In the app there are posts like SO posts and also the flags for posts as Github flags or marks, whatever the correct term for it. A post can have only one flag at a time. There are plenty of posts ever increasing, but four or five flags and they will not increase.
First approach, normalized; I have modeled this part of my data with three tables; two for the corresponding entities posts and flags, and one for the relationship as post_flag. No reference in any of the entity tables mentioned to the other entity table for relationship. All relationship is recorded in the relationship table post_flag, and that is only the id pair for ids of a post and a flag.
Table structure in that case would be:
CREATE TABLE posts
(
id bigserial PRIMARY KEY,
created_at timestamp without time zone NOT NULL DEFAULT CURRENT_TIMESTAMP,
title character varying(100),
text text,
score integer DEFAULT 0,
author_id integer NOT NULL REFERENCES users (id),
product_id integer NOT NULL REFERENCES products (id),
);
CREATE TABLE flags
(
id bigserial PRIMARY KEY,
created_at timestamp without time zone NOT NULL DEFAULT CURRENT_TIMESTAMP,
flag character varying(30) NOT NULL -- planned, in progress, fixed
);
CREATE TABLE post_flag
(
created_at timestamp without time zone NOT NULL DEFAULT CURRENT_TIMESTAMP,
post_id integer NOT NULL REFERENCES posts (id),
flag_id integer NOT NULL REFERENCES flags (id)
);
To get posts flagged as fixed I have to use:
-- homepage posts- fixed posts tab
SELECT
p.*,
f.flag
FROM posts p
JOIN post_flag p_f
ON p.id = p_f.post_id
JOIN flags f
ON p_f.flag_id = f.id
WHERE f.flag = 'fixed'
ORDER BY p_f.created_at DESC
Second approach; I have two tables posts and flags. The table posts has a flag_id column that references a flag in the flags table.
CREATE TABLE posts
(
id bigserial PRIMARY KEY,
created_at timestamp without time zone NOT NULL DEFAULT CURRENT_TIMESTAMP,
title character varying(100),
text text,
score integer DEFAULT 0,
author_id integer NOT NULL REFERENCES users (id),
product_id integer NOT NULL REFERENCES products (id),
flag_id integer DEFAULT NULL REFERENCES flags (id)
);
CREATE TABLE flags
(
id bigserial PRIMARY KEY,
created_at timestamp without time zone NOT NULL DEFAULT CURRENT_TIMESTAMP,
flag character varying(30) NOT NULL -- one of planned, in progress, fixed
);
For same data;
-- homepage posts- fixed posts tab
SELECT
p.*,
f.flag
FROM posts p
JOIN flags f
ON p.flag_id = f.id
WHERE f.flag = 'fixed'
ORDER BY p.created_at DESC
Third approach denormalized; I have only one table posts. Posts table has a flag column to store the flag assigned to the post.
CREATE TABLE posts
(
id bigserial PRIMARY KEY,
created_at timestamp without time zone NOT NULL DEFAULT CURRENT_TIMESTAMP,
title character varying(100),
text text,
score integer DEFAULT 0,
author_id integer NOT NULL REFERENCES users (id),
product_id integer NOT NULL REFERENCES products (id),
flag character varying(30)
);
Here I would only have for same data;
-- homepage posts- fixed posts tab
SELECT
p.*,
FROM posts p
WHERE p.flag = 'fixed'
ORDER BY p.created_at DESC
I wonder if first approach is an overkill in terms of normalization of data in a RDBMS like Postgresql? For a post comment relationship that first approach would be great and indeed I make use of it. But I have some very few quantity data used as meta data for posts as badges, flags, tags. As you see in fact in the most normal form, the first approach, I already use some product_id etc for a using one less JOIN but to another table as a different relation, not to the flags. So, there my approach fits into my second approach. Should I use the more denormalized approach, the third one, having posts table and a flag column in it? What is the better approach in terms of performance, expansion, and maintainability?
Use the second approach.
The first is a many-to-many data structure and you say
A post can have only one flag at a time.
So you would then have to build the business logic in to the front-end or set up complex rules to check a post never have more than one flag.
The third approach will result in messy data, again unless you implement checks or rules to ensure the flags are not misspelled or new ones added.
Expansion and maintainability are provided in the second approach; it is also self documenting. Worry about performance when it actually becomes a problem, and not before.
Personally I would make the flag_id field in the posts table NULL, which would allow you to model a post without a flag.
Blending two approaches
Assuming your flag names are unique, you can use the flag name as a natural key. Your table structures would then be
CREATE TABLE posts
(
id bigserial PRIMARY KEY,
... other fields
flag character varying(30) REFERENCES flags (flag)
);
CREATE TABLE flags
(
flag character varying(30) NOT NULL PRIMARY KEY,
created_at timestamp without time zone NOT NULL DEFAULT CURRENT_TIMESTAMP
);
You then get the benefit of being able to write queries for flag without having to JOIN to the flags table while having flag names checked by the table reference.

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...

Serial numbers per group of rows for compound key

I am trying to maintain an address history table:
CREATE TABLE address_history (
person_id int,
sequence int,
timestamp datetime default current_timestamp,
address text,
original_address text,
previous_address text,
PRIMARY KEY(person_id, sequence),
FOREIGN KEY(person_id) REFERENCES people.id
);
I'm wondering if there's an easy way to autonumber/constrain sequence in address_history to automatically count up from 1 for each person_id.
In other words, the first row with person_id = 1 would get sequence = 1; the second row with person_id = 1 would get sequence = 2. The first row with person_id = 2, would get sequence = 1 again. Etc.
Also, is there a better / built-in way to maintain a history like this?
Don't. It has been tried many times and it's a pain.
Use a plain serial or IDENTITY column:
Auto increment table column
CREATE TABLE address_history (
address_history_id serial PRIMARY KEY
, person_id int NOT NULL REFERENCES people(id)
, created_at timestamp NOT NULL DEFAULT current_timestamp
, previous_address text
);
Use the window function row_number() to get serial numbers without gaps per person_id. You could persist a VIEW that you can use as drop-in replacement for your table in queries to have those numbers ready:
CREATE VIEW address_history_nr AS
SELECT *, row_number() OVER (PARTITION BY person_id
ORDER BY address_history_id) AS adr_nr
FROM address_history;
See:
Gap-less sequence where multiple transactions with multiple tables are involved
Or you might want to ORDER BY something else. Maybe created_at? Better created_at, address_history_id to break possible ties. Related answer:
Column with alternate serials
Also, the data type you are looking for is timestamp or timestamptz, not datetime in Postgres:
Ignoring time zones altogether in Rails and PostgreSQL
And you only need to store previous_address (or more details), not address, nor original_address. Both would be redundant in a sane data model.

MySQL - Prevent duplicate records in table via index?

Using MySQL 5
I have a table like this:
date (varchar)
door (varchar)
shift (varchar)
route (varchar)
trailer (varchar)
+ other fields
This table contains user generated content (copied in from another 'master' table) and to prevent the users from creating the same data more than 1x the table has a unique index created based on the fields specified above.
The problem is that the "duplicate prevention" index doesn't work.
Users can still add in duplicate records with no errors being reported.
Is this problem due to my not understanding something about how indexes work?
Or
Is it a possible conflict with the primary key field (autoincrementing int)?
The CREATE TABLE looks like this:
CREATE TABLE /*!32312 IF NOT EXISTS*/ "tableA" (
"Date" varchar(12) default NULL,
"door" varchar(12) default NULL,
"Shift" varchar(45) default NULL,
"route" varchar(20) default NULL,
"trailer" varchar(45) default NULL,
"fieldA" varchar(45) default NULL,
"fieldB" varchar(45) default NULL,
"fieldC" varchar(45) default NULL,
"id" int(10) unsigned NOT NULL auto_increment,
PRIMARY KEY ("id"),
UNIQUE KEY "duplicate_preventer" ("Date","door","Shift","route","trailer"),
A row duplicated is:
date door shift route trailer
10/4/2009 W17 1st Shift TEST-01 NULL
10/4/2009 W17 1st Shift TEST-01 NULL
Users can still add in duplicate records with no errors being reported.
What do you mean by "duplicate records"?
Depending on collation, case, accent etc. may matter, and 'test' and 'TEST' will not be considered duplicates.
Could you please post the results of SHOW CREATE TABLE mytable?
Also, could you please run this query:
SELECT date, door, shift, route, trailer
FROM mytable
GROUP BY
date, door, shift, route, trailer
HAVING COUNT(*) > 1
If it returns the rows, the problem is with the index; if it does not, the problem is with your definition of a "duplicate".
Update:
Your columns allow NULLs.
NULL values in MySQL are not considered duplicate from the point of view of a UNIQUE index:
CREATE TABLE testtable (door VARCHAR(20), shift VARCHAR(15), UNIQUE KEY (door, shift));
INSERT
INTO testtable
VALUES
('door', NULL),
('door', NULL);
SELECT door, shift
FROM testtable
GROUP BY
door, shift
HAVING COUNT(*) > 1;
From documentation:
A UNIQUE index creates a constraint such that all values in the index must be distinct. An error occurs if you try to add a new row with a key value that matches an existing row. This constraint does not apply to NULL values except for the BDB storage engine. For other engines, a UNIQUE index allows multiple NULL values for columns that can contain NULL. If you specify a prefix value for a column in a UNIQUE index, the column values must be unique within the prefix.
Are you sure that you are using unique index instead of a normal index?
create unique index uix on my_table (date, door, shift, route, trailer);
Also that kind of index only makes sure that combination of fields is unique, you can for example have several duplicate dates if, for example, field door is different on every row. The difference could something that is hard to spot, for example a space in end of the value or lowercase/uppercase difference.
Update: your unique index seems to be in order. The problem is elsewhere.
I think you'd want to create a unique constraint on the fields you don't want duplicated. This will in turn create a unique index.
Like this:
ALTER TABLE YourTable
ADD CONSTRAINT uc_yourconstraintname UNIQUE (date, door, shift, route, trailer)