Is it possible to create a DEFAULT clause with an incrementing identifier? - postgresql

I would like to know whether it is possible to achieve something like the following in PostgreSQL:
I have a table named Dossier (means "folder" in English), with the the following table structure: name, taille,...
Now, what I'd like to have on my table is a condition such that each time a new instance of Dossier is created, the value in the name column is automatically supplemented with a self-incrementing identifier like so: ref00001, ref00002 etc. (To clarify, after the second insertion, the value for the name column should be ref00002 automatically...)

CREATE SEQUENCE seq_ref;
CREATE TABLE dossier
(
ref TEXT NOT NULL PRIMARY KEY DEFAULT 'ref' || NEXTVAL('seq_ref'),
value TEXT
);
If you want zero-padded numbers, use this:
CREATE SEQUENCE seq_ref;
CREATE TABLE dossier
(
ref TEXT NOT NULL PRIMARY KEY DEFAULT 'ref' || LPAD(NEXTVAL('seq_ref')::TEXT, 10, '0'),
value TEXT
);

Related

Table doesn't creates with default value [SQLalchemy]

I have table
users = Table(
"users",
metadata,
Column("id", Integer, primary_key=True),
Column("username", String(32), unique=True),
Column("password", String(64)),
Column("games_all", Integer, default=0),
Column("games_won", Integer, default=0),
Column("created_at", DateTime, default=datetime.utcnow)
)
and when i do
metadata.create_all(bind=engine)
the table is created but with an empty default field.
Image
I want the table to be created with the default values that I specified.
The column default argument is the default value set on the Python side when creating a users instance. If you want the default value to be set on the database side, use server_default.

how to generate auto increment column which will be have some pattern in postgresql?

I have a table named dept_registration it has a column dept_code so I have to make this field auto-generate but in a specific pattern.
example:- test0001
test0002
test0003
test0004
the "test" should be appended before number automatically after insertion
The column definition could be
dept_code text DEFAULT 'test' || lpad(nextval('myseq')::text, 4, '0')
where myseq is a sequence.
You will get in trouble once the counter reaches 10000...

postgresql : search records based on array field vaule with multiple values

I have a table that has an array field.
CREATE TABLE notifications
(
id integer NOT NULL DEFAULT nextval('notifications_id_seq'::regclass),
title character(100) COLLATE pg_catalog."default" NOT NULL,
tags text[] COLLATE pg_catalog."default",
CONSTRAINT notifications_pkey PRIMARY KEY (id)
)
and tags field can have multiple values from
["a","b","c","d"]
now I want all the records for which tags have a or d ("a","d")array values.
I can use postgresl in but this can be used to search single value. How can I achieve this?
You could use ANY:
SELECT *
FROM notifications
WHERE 'a' = ANY(tags) OR 'b' = ANY(tags);
DBFiddle Demo
If the values 'a' and 'b' are static (you only need to check for those 2 values in every query), then you can go with the solution that Lukasz Szozda provided.
But if the values you want to check for are dynamic and are different in multiple queries(sometimes it is {'a','b'} but sometimes it is {'b', 'f','m'}) you can create an intersection of both of the arrays and check if the intersection is empty.
For example:
If we have the following table and data:
CREATE TABLE test_table_1(description TEXT, tags TEXT[]);
INSERT INTO test_table_1(description, tags) VALUES
('desc1', array['a','b','c']),
('desc2', array['c','d','e']);
If we want to get all of the rows from test_table_1 that have one of the following tags b, f, or m, we could do it with the following query:
SELECT * FROM test_table_1 tt1
WHERE array_length((SELECT array
(
SELECT UNNEST(tt1.tags)
INTERSECT
SELECT UNNEST(array['b','f','m'])
)), 1) > 0;
In the query above we use array_length to check if the intersection is empty.
Writing the query this way can also be useful if you want to add additional constraint to the number of matched tags.
For example if you want to get all of the rows that have at least 2 tags from the group {'a','b','c'} you just need to set array_length(...) > 1

CSV file data into a PostgreSQL table

I am trying to create a database for movielens (http://grouplens.org/datasets/movielens/). We've got movies and ratings. Movies have multiple genres. I splitted those out into a separate table since it's a 1:many relationship. There's a many:many relationship as well, users to movies. I need to be able to query this table multiple ways.
So I created:
CREATE TABLE genre (
genre_id serial NOT NULL,
genre_name char(20) DEFAULT NULL,
PRIMARY KEY (genre_id)
)
.
INSERT INTO genre VALUES
(1,'Action'),(2,'Adventure'),(3,'Animation'),(4,'Children\s'),(5,'Comedy'),(6,'Crime'),
(7,'Documentary'),(8,'Drama'),(9,'Fantasy'),(10,'Film-Noir'),(11,'Horror'),(12,'Musical'),
(13,'Mystery'),(14,'Romance'),(15,'Sci-Fi'),(16,'Thriller'),(17,'War'),(18,'Western');
.
CREATE TABLE movie (
movie_id int NOT NULL DEFAULT '0',
movie_name char(75) DEFAULT NULL,
movie_year smallint DEFAULT NULL,
PRIMARY KEY (movie_id)
);
.
CREATE TABLE moviegenre (
movie_id int NOT NULL DEFAULT '0',
genre_id tinyint NOT NULL DEFAULT '0',
PRIMARY KEY (movie_id, genre_id)
);
I dont know how to import my movies.csv with columns movie_id, movie_name and movie_genre For example, the first row is (1;Toy Story (1995);Animation|Children's|Comedy)
If I INSERT manually, it should be look like:
INSERT INTO moviegenre VALUES (1,3),(1,4),(1,5)
Because 3 is Animation, 4 is Children and 5 is Comedy
How can I import all data set this way?
You should first create a table that can ingest the data from the CSV file:
CREATE TABLE movies_csv (
movie_id integer,
movie_name varchar,
movie_genre varchar
);
Note that any single quotes (Children's) should be doubled (Children''s). Once the data is in this staging table you can copy the data over to the movie table, which should have the following structure:
CREATE TABLE movie (
movie_id integer, -- A primary key has implicit NOT NULL and should not have default
movie_name varchar NOT NULL, -- Movie should have a name, varchar more flexible
movie_year integer, -- Regular integer is more efficient
PRIMARY KEY (movie_id)
);
Sanitize your other tables likewise.
Now copy the data over, extracting the unadorned name and the year from the CSV name:
INSERT INTO movie (movie_id, movie_name)
SELECT parts[1], parts[2]::integer
FROM movies_csv, regexp_matches(movie_name, '([[:ascii:]]*)\s\(([\d]*)\)$') p(parts)
Here the regular expression says:
([[:ascii:]]*) - Capture all characters until the matches below
\s - Read past a space
\( - Read past an opening parenthesis
([\d]*) - Capture any digits
\) - Read past a closing parenthesis
$ - Match from the end of the string
So on input "Die Hard 17 (John lives forever) (2074)" it creates a string array with {'Die Hard 17 (John lives forever)', '2074'}. The scanning has to be from the end $, assuming all movie titles end with the year of publication in parentheses, in order to preserve parentheses and numbers in movie titles.
Now you can work on the movie genres. You have to split the string on the bar | using the regex_split_to_table() function and then join to the genre table on the genre name:
INSERT INTO moviegenre
SELECT movie_id, genre_id
FROM movies_csv, regexp_split_to_table(movie_genre, '\|') p(genre) -- escape the |
JOIN genre ON genre.genre_name = p.genre;
After all is done and dusted you can delete the movies_csv table.

How to increase sequence only if column's value is different from the previous? PostgreSQL

Exactly as in the topic. For really easy example I have table:
Cars(
TEXT make,
INTEGER value,
TEXT owner
);
With records like this:
('BMW', 100000, 'AcmeCompany')
('BMW', 350000, 'XCompany')
('Lamborgini', 500000, 'YCompany')
('Fiat', 30000, 'ZCompany')
('BMW', 200000, 'XCompany')
and I want to create table with column makeID (don't ask why, it's just a example :P)
Informations(
INTEGER makeID
other_stuff
)
I have sequence on makeID and I have to increment sequence ONLY when I have new value of make in in my SELECT (it's like transtalion from string into integer; if it is possible I don't wanna sha1 or other cryptografic things just a simply sequence!)
INSERT INTO Informations(makeID) (
-- IF I have new value: SELECT nextVal('mysequence')
-- ELSE : SELECT currval('mysequence')
)
I know it should be possible by pgsql function (sort table by 'make' column, then save last value of 'make' into temp variable and do INSERT INTO with check value of my actual 'make' value with value in temp for incrementing sequence variable or not). But there is easier way, just by query? Combination of join, sort and distinct?
Not sure I understand. It would be simply serial with select distinct
create table make (
make_id serial,
make_brand text,
make_owner text
);
insert into make (make_brand, make_owner)
select distinct make, owner
from cars;