Can I use two column on EXCLUDE USING gist? - postgresql

I am using EXCLUDE USING gist to block users to visit the same place twice unless 6 months have pass, but with my code it blocks user of going ANYWHERE unless 6 months have pass
CREATE SCHEMA code
CREATE TABLE code.place (
pk_place_id VARCHAR(8),
place_name VARCHAR (50),
CONSTRAINT pk_place_id PRIMARY KEY (pk_place_id)
);
CREATE TABLE code.user (
pk_user_id VARCHAR(3),
user_name VARCHAR (50),
CONSTRAINT pk_user_id PRIMARY KEY (pk_user_id)
);
CREATE TABLE code.visit (
pk_user_id VARCHAR(3),
pk_place_id VARCHAR(8),
data DATE,
CONSTRAINT pk_user_id FOREIGN KEY (pk_user_id) REFERENCES code.user,
CONSTRAINT pk_place_id FOREIGN KEY (pk_place_id) REFERENCES code.place,
EXCLUDE USING gist (pk_user_id WITH =, daterange(data, (data + interval '6 months')::date) WITH &&)
);

I would like to thanks Laurenz Albe and Miles Elam for the help.
Here is the answer:
Yes you can add as many columns as you like to the table.
Here is one with just two column
EXCLUDE USING gist (user WITH =, data WITH &&)
Here is one with just three column
EXCLUDE USING gist (user WITH =, place WITH =, data WITH &&)
To summarize, just add the name of the column you like and then type WITH =,

Related

Trying to make a UNIQUE KEY become a FOREIGN KEY, but it says error

I created this table. I want the user_id to be inserted without someone having to type it. Can someone help me? When I tried to create it it shows this error:
Why is the fk_id being compared to cpf?
CREATE TABLE code.went_to
(
user_id INTEGER,
cpf VARCHAR(11) NOT NULL,
cep VARCHAR(8) NOT NULL,
date DATE,
contact_id INTEGER,
CONSTRAINT fk_id
FOREIGN KEY (user_id) REFERENCES code.user,
CONSTRAINT fk_cpf_user
FOREIGN KEY (cpf) REFERENCES code.user,
CONSTRAINT fk_cep_place
FOREIGN KEY (cep) REFERENCES code.place,
CONSTRAINT fk_tipo_id
FOREIGN KEY (tipo_id) REFERENCES code.contact,
EXCLUDE USING gist (cpf WITH =, cep WITH =, daterange(data, (data + interval '4 months')::date) WITH &&),
EXCLUDE USING gist (cep WITH =, contact_id WITH =, daterange(data, (data + interval '1 months')::date) WITH &&)
);
The table that user_id comes from:
CREATE TABLE code.user
(
user_id SERIAL NOT NULL,
cpf VARCHAR(11) NOT NULL,
name CHAR(75) NOT NULL,
nick_name VARCHAR(15),
CONSTRAINT pk_cpf PRIMARY KEY(cpf),
CONSTRAINT un_id UNIQUE (id),
CONSTRAINT un_nick_name UNIQUE (nick_name)
);
Sorry if its stated weird, my English is not the best. But essentially the question should be:
How could I import data (user_id) from one table (user) into another table (went_to) based on the primary key (cpf)?
If you don't specify a target column, the references clause assumes the primary key of the target table. There is no matching on the name happening.
You need to include the column of the unique key in your foreign key definition:
CONSTRAINT fk_id FOREIGN KEY (user_id) REFERENCES code."user"(user_id)
To make things less confusing, I would also do that for the FK that references the PK
CONSTRAINT fk_cpf_user FOREIGN KEY (cpf) REFERENCES code."user"(cpf),

No overlapping data

Being a new user of postgres, I have created a database in postgres 13. It contains tables
including 4 Fields
ID integer (PK)
HoleID varchar(20)
From numeric NOT NULL CHECK (From>=0)
To numeric
Cat varchar (20)
I want to create a constraint that will check that for an identical entity number, the From and To fields of a record must not overlap with another record.
I have proposed the exclude constraint below but it does not work
ADD CONSTRAINT no_overlap EXCLUDE USING GIST ("HoleID" WITH =, ("mFrom", "mTo") WITH &&);
Thank you for helping me.
This is easier with a single numrange column instead of from/to.
create table thing (
id bigserial primary key,
holeid varchar(20),
range numrange not null,
exclude using gist (range with &&, holeid with =)
);
insert into thing (holeid, range) values
('foo', '[1, 10]'::numrange),
-- ok, same holeid, does not overlap
('foo', '[10.1, 11]'::numrange),
-- ok, different holeid, overlaps.
('bar', '[2,5]'::numrange),
-- not ok, same holeid, overlaps.
('foo', '[0, 1]'::numrange);
Demonstration.
Note that [] is inclusive and () is exclusive. (1,10)::numrange and (10,20)::numrange do not overlap. [1,10]::numrange and [10,20]::numrange do overlap.
You can create the constraint like this:
ALTER TABLE tab
ADD CONSTRAINT no_overlap EXCLUDE USING gist (
"HoleID" WITH =,
numrange("mFrom", "mTo") WITH &&
);
You should never store a timestamp as number.

Postgres constraint name need to be unique across single table or entire schema?

I'm trying to understand why some Postgres constraints can be named the same within different tables, and some don't
Here a simple example:
drop table if exists public.table_1;
drop table if exists public.table_2;
CREATE TABLE public.table_1 (
id serial NOT NULL,
date_start date NOT NULL,
date_end date NULL
);
CREATE TABLE public.table_2 (
id serial NOT NULL,
date_start date NOT NULL,
date_end date NULL
);
alter table public.table_1 add constraint my_constraint_1 check (date_start > now());
alter table public.table_2 add constraint my_constraint_1 check (date_start > now());
alter table public.table_1 add constraint my_constraint_2 EXCLUDE USING gist (daterange(date_start, coalesce(date_end, 'infinity'), '[]') WITH &&);
alter table public.table_2 add constraint my_constraint_2 EXCLUDE USING gist (daterange(date_start, coalesce(date_end, 'infinity'), '[]') WITH &&);
As you can see I can use the same name my_constraint_1 with different tables
Why the name my_constraint_1 can be used as the same in different tables, while my_constraint_2 must be unique otherwise I get the error Errore SQL [42P07]: ERROR: relation "my_constraint_2" already exists?
Why the name my_constraint_1 can be used as the same in different tables, while my_constraint_2 must be unique
Constraint 2 has underlying index with the same name, while constraint 1 is simple check contraint on table level.
EXCLUDE
Exclusion constraints are implemented using an index, so each specified operator must be associated with an appropriate operator class (see Section 11.10) for the index access method index_method.
CREATE INDEX my_constraint_2 ON public.table_1 USING gist (daterange(date_start, COALESCE(date_end, 'infinity'::date), '[]'::text))
db<>fiddle demo

How to restrict values in a column based on values from another column in PostgreSQL?

I would like to prevent a mismatch between course_code and course_namewhen inserting values to the table below.
CREATE TABLE course (
course_id INT4 NOT NULL PRIMARY KEY,
course_code CHAR(4) NOT NULL,
course_name VARCHAR(30) NOT NULL
);
For both I created an enumeration (see below), now I want to link 'C101' to 'Computer Science' etc.
CREATE TYPE e_course_code AS ENUM (
'C101',
'B102',
'E103',
'V104',
'A105',
'E104'
);
CREATE TYPE e_course_name AS ENUM (
'Computer Science',
'Business Information Management',
'Electronics',
'Visual Programming',
'Audio Technology',
'Engineering'
);
Is it possible to link specified (enumerated) values for two (or even more) columns? Something that returns an error message when inserting a course_code and course_name that do not match?
The fast and reliable tool to implement what you ask in the title would be a foreign key constraint with MATCH FULL:
CREATE TABLE course (
course_code char(4) PRIMARY KEY
, course_name text NOT NULL
);
CREATE TABLE some_other_table (
some_other_id serial PRIMARY KEY
, course_code char(4)
, course_name text
, -- more columns
, CONSTRAINT course_fk FOREIGN KEY (course_code, course_name)
REFERENCES course(course_code, course_name) MATCH FULL
);
Related:
MATCH FULL vs MATCH SIMPLE in foreign key constraints
However, some_other_table.course_name would be completely redundant, and the clean implementation would be the normalized form instead:
CREATE TABLE some_other_table (
some_other_id serial PRIMARY KEY
, course_code char(4)
, -- more columns
, CONSTRAINT course_fk FOREIGN KEY (course_code) REFERENCES course(course_code)
);
Or you add course_id as PK to the course table and use that as FK column.
You can always add a VIEW to display course_name additionally.
The simplest way to solve this (as i see it) would make two separate tables- one with id and code, another with code and name. See this question - Difference between 3NF and BCNF in simple terms (must be able to explain to an 8-year old) - the example in the answer is similar to your problem.

Timetravel in postgres - violating PRIMARY KEY constraint

I wanted to use timetravel function (F.39. spi, PostgreSQL 9.1 Documentation) in my application, however it doesn't seem to work properly for me. With inserting rows into table everything works just fine, I get start and stop date properly, but when I'm trying to update those rows postgres gives me error about violating of PRIMARY KEY constraint. He's trying to insert a tuple with the same primary id as previous tuple...
It's insane to remove primary key constraints from all tables in the database but it's the functionality I need. So maybe you have some expierience with timetravel?
Any sort of help will be appreciated. Thanks in advance.
DDL:
CREATE TABLE cities
(
city_id serial NOT NULL,
state_id integer,
name character varying(80) NOT NULL,
start_date abstime,
stop_date abstime,
CONSTRAINT pk_cities PRIMARY KEY (city_id ),
CONSTRAINT fk_cities_states FOREIGN KEY (state_id)
REFERENCES states (state_id) MATCH SIMPLE
ON UPDATE CASCADE ON DELETE NO ACTION
)
WITH (
OIDS=FALSE
);
-- Trigger: time_travel on cities
-- DROP TRIGGER time_travel ON cities;
CREATE TRIGGER time_travel
BEFORE INSERT OR UPDATE OR DELETE
ON cities
FOR EACH ROW
EXECUTE PROCEDURE timetravel('start_date', 'stop_date');
STATEMENT GIVEN:
INSERT INTO cities(
state_id, name)
VALUES (20,'Paris');
and that's ok. I get start_date and stop_date.
But by:
UPDATE cities SET name='Rome' WHERE name='Paris'
I get error- described earlier.
Schema of states
-- Table: states
-- DROP TABLE states;
CREATE TABLE states
(
state_id serial NOT NULL, -- unikatowy numer wojewodztwa
country_id integer, -- identyfikator panstwa, w ktorym znajduje sie wojewodztwo
name character varying(50), -- nazwa wojewodztwa
CONSTRAINT pk_states PRIMARY KEY (state_id ),
CONSTRAINT uq_states_state_id UNIQUE (state_id )
)
WITH (
OIDS=FALSE
);
Unfortunately,as a new user I'm not allowed to post images here.
You can see them there:
Sample data from table cities: korpusvictifrew.cba.pl/postgres_cities.png
Sample data from table states: korpusvictifrew.cba.pl/states_data.png
Time travel converts an UPDATE into an UPDATE of the old record's stop_date and an INSERT of a new one with the changed data plus an infinity stop_date. You can't have more than one record for city_id due to pk_cities. The time travel triggers do not allow you to break that requirement.
You cannot use this:
CONSTRAINT pk_cities PRIMARY KEY (city_id )
You must use this
CONSTRAINT pk_cities PRIMARY KEY (city_id, stop_date)