How to prevent overlapping of int ranges - postgresql

I have a table as follow :
CREATE TABLE appointments (
id SERIAL PRIMARY KEY,
date TIMESTAMP NOT NULL,
start_mn INT NOT NULL,
end_mn INT NOT NULL,
EXCLUDE using gist((array[start_mn, end_mn]) WITH &&)
)
I want to prevent start_mn and end_mn overlapping between rows so I've added a gist exclusion :
EXCLUDE using gist((array[start_mn, end_mn]) WITH &&)
But inserting the two following do not trigger the exclusion:
INSERT INTO appointments(date, start_mn, end_mn) VALUES('2020-08-08', 100, 200);
INSERT INTO appointments(date, start_mn, end_mn) VALUES('2020-08-08', 90, 105);
How can I achieve this exclusion ?

If you want to prevent an overlapping range you will have to use a range type not an array.
I also assume that start and end should never overlap on the same day, so you need to include the date column in the exclusion constraint:
CREATE TABLE appointments
(
id SERIAL PRIMARY KEY,
date TIMESTAMP NOT NULL,
start_mn INT NOT NULL,
end_mn INT NOT NULL,
EXCLUDE using gist( int4range(start_mn, end_mn, '[]') WITH &&, "date" with =)
)
If start_mn and end_mn are supposed to be "time of the day", then those columns should be defined as time, not as integers.

Related

Create exclusion constraint with non commutative operator in Postgres

I have a question about exclusion constraint.
I have a following table:
-- auto-generated definition
create table archives_seasonmodel
(
id serial not null
constraint archives_seasonmodel_pkey
primary key,
series_id integer not null
constraint archives_seasonmodel_series_id_e05c6f84_fk_archives_
references archives_tvseriesmodel
deferrable initially deferred,
last_watched_episode smallint
constraint archives_seasonmodel_last_watched_episode_check
check (last_watched_episode >= 0),
season_number smallint not null
constraint archives_seasonmodel_season_number_check
check (season_number >= 0)
constraint season_number_gte_1_check
check (season_number >= 1),
_order integer not null,
number_of_episodes smallint not null
constraint archives_seasonmodel_number_of_episodes_check
check (number_of_episodes >= 0),
episodes hstore,
translation_years daterange not null,
constraint archives_seasonmodel_series_id_season_number_4368dab7_uniq
unique (series_id, season_number),
constraint last_watched_episode_and_number_of_episodes_are_gte_one
check (((last_watched_episode >= 1) OR (last_watched_episode IS NULL)) AND (number_of_episodes >= 1)),
constraint mutual_watched_episode_and_number_of_episodes_check
check (number_of_episodes >= last_watched_episode)
);
alter table archives_seasonmodel
owner to postgres;
create index archives_seasonmodel_series_id_e05c6f84
on archives_seasonmodel (series_id);
create index archives_seasonmodel_series_id_season_number_4368dab7_idx
on archives_seasonmodel (series_id, season_number);
create index exclude_overlapping_seasons_translation_time_check
on archives_seasonmodel (translation_years, series_id);
In general this table contains 3 specific columns:
series_id - positive integer, foreign key to table archives_tvseriesmodel
season_number – positive integer, number of season in series
translation_years - daterange, translation dates range of each season in series
General idea:
Series contains multiple seasons.
Each season has season_number (from 1 to infinity) that represents obviously number of seasons in series.
Each season has translation_years daterange which represent start and end date of each season.
There are exclusion constraint ‘exclude_overlapping_seasons_translation_time_check thats prevents
translation_years dateranges from overlapping each other.
But there are another problem in validation. I need to maintain translation_years in a such way that season with number for example 4 has datarange fully lower then season 5.
For example:
correct:
season_number =4 , translation_years =(2012-01-01, 2013-01-01)
season_number =5 , translation_years =(2013-03-01, 2014-01-01)
incorrect:
season_number =4 , translation_years =(2012-01-01, 2013-01-01)
season_number =5 , translation_years =(2010-01-01, 2011-01-01)
what I tried to do:
ALTER TABLE archives_seasonmodel
ADD CONSTRAINT test
EXCLUDE USING gist(series_id WITH =, translation_years WITH <<)
WHERE (season_number = season_number - 1 )
But it says that only commutative operator are allowed in constrains, and << is not a commutative one.
Question is is it possible to make a such constraint somehow?
Thank you
Perhaps instead of strictly left operator (<<) try with the range overlap operator (&&). See Range Functions. NOT TESTED as I do not have a DB available at the moment.
alter table archives_seasonmodel
add constraint test
exclude using gist(series_id with =, translation_years with &&)
where (season_number = season_number - 1 );

Postgresql SQL throws ambiguous column error

I have the following table in my Postgres database
CREATE TABLE "public"."zuffs"
(
"hash" bigint NOT NULL,
"zuff" BIGINT NOT NULL,
"lat" INTEGER NOT NULL,
"lng" INTEGER NOT NULL,
"weather" INTEGER DEFAULT 0,
"expires" INTEGER DEFAULT 0,
"clients" INTEGER DEFAULT 0,
CONSTRAINT "zuffs_hash" PRIMARY KEY ("hash")
) WITH (oids = false);
to which I want to add a new row or update the weather, expires & clients columns if the row already exists. To do this I get my PHP script to generate the following SQL
INSERT INTO zuffs (hash,zuff,lat,lng,weather,expires)
VALUES(5523216,14978310951341,4978,589,105906435,4380919) ON CONFLICT(hash) DO UPDATE SET
weather = 105906435,expires = 4380919,clients = clients + 1;
which fails with the error
ERROR: column reference "clients" is ambiguous
I fail to see why this might be happening. I hope that someone here can explain
In the UPDATE part you should use the EXCLUDED "row" to reference the values. And to reference the existing value, you need to prefix the column with the table again to avoid the ambiguity between "excluded" and "current" values:
INSERT INTO zuffs (hash,zuff,lat,lng,weather,expires)
VALUES (5523216,14978310951341,4978,589,105906435,4380919)
ON CONFLICT(hash) DO UPDATE
SET weather = excluded.weather,
expires = excluded.expires,
clients = zuffs.clients + 1;

Efficient way to reconstruct base table from changes

I have a table consisting of products (with ID's, ~15k records) and another table price_changes (~88m records) recording a change in the price for a given productID at a given changedate.
I'm now interested in the price for each product at given points in time (say every 2 hours for a year, so altogether ~ 4300 points; altogether resulting in ~64m data points of interest). While it's very straight forward to determine the price for a given product at a given time, it seems to be quite time-consuming to determine all 64m data points.
My approach is to pre-populate a new target table fullprices with the data points of interest:
insert into fullprices(obsdate,productID)
select obsdate, productID from targetdates, products
and then update each price observation in this new table like this:
update fullprices f set price = (select price from price_changes where
productID = f.productID and date < f.obsdate
order by date desc
limit 1)
which should give me the most recent price change in each point in time.
Unfortunately, this takes ... well, ages. Is there any better way to do it?
== Edit: My tables are created as follows: ==
CREATE TABLE products
(
productID uuid NOT NULL,
name text NOT NULL,
CONSTRAINT products_pkey PRIMARY KEY (productID )
);
CREATE TABLE price_changes
(
id integer NOT NULL,
productID uuid NOT NULL,
price smallint,
date timestamp NOT NULL
);
CREATE INDEX idx_pc_date
ON price_changes USING btree
(date);
CREATE INDEX idx_pc_productID
ON price_changes USING btree
(productID);
CREATE TABLE targetdates
(
obsdate timestamp
);
CREATE TABLE fullprices
(
obsdate timestamp NOT NULL,
productID uuid NOT NULL,
price smallint
);

How do I add a constraint with a where clause in PostgreSQL?

I have a table with reservations. A reservation is made of a date range, and a time range. They also belong to a couple of other models. I would like to add a constraint that makes it impossible for a reservation to happen for overlapping times.
I have this:
CREATE TABLE reservations (
id integer NOT NULL,
dates daterange,
times timerange,
desk_id integer NOT NULL,
space_id integer,
);
ALTER TABLE reservations ADD EXCLUDE USING gist (dates WITH &&, times WITH &&)
It works well. But I want this constraint to be scoped to desk_id and client_id.
It should be possible to save a record for overlapping times/dates when this record is about different desk_id or space_id.
How can I do this?
You just can use the exact same mechanism you were using, but also adding desk_id and space_id to your exclusions. This time, instead of using the && operator (meaning overlaps) with the = operator:
ALTER TABLE reservations
ADD EXCLUDE
USING gist (desk_id WITH =, space_id WITH =, dates WITH &&, times WITH &&) ;
Theses inserts will work, because they involve two different desk_id:
INSERT INTO
reservations
(id, dates, times, desk_id, space_id)
VALUES
(1, '[20170101,20170101]'::daterange, '[10:00,11:00]'::timerange, 10, 10),
(2, '[20170101,20170101]'::daterange, '[10:30,11:00]'::timerange, 20, 10) ;
This insert will fail, because you'd be having a time-range overlap, and the same desk_id and space_id:
INSERT INTO
reservations
(id, dates, times, desk_id, space_id)
VALUES
(3, '[20170101,20170101]'::daterange, '[10:00,11:00]'::timerange, 10, 10) ;

an empty row with null-like values in not-null field

I'm using postgresql 9.0 beta 4.
After inserting a lot of data into a partitioned table, i found a weird thing. When I query the table, i can see an empty row with null-like values in 'not-null' fields.
That weird query result is like below.
689th row is empty. The first 3 fields, (stid, d, ticker), are composing primary key. So they should not be null. The query i used is this.
select * from st_daily2 where stid=267408 order by d
I can even do the group by on this data.
select stid, date_trunc('month', d) ym, count(*) from st_daily2
where stid=267408 group by stid, date_trunc('month', d)
The 'group by' results still has the empty row.
The 1st row is empty.
But if i query where 'stid' or 'd' is null, then it returns nothing.
Is this a bug of postgresql 9b4? Or some data corruption?
EDIT :
I added my table definition.
CREATE TABLE st_daily
(
stid integer NOT NULL,
d date NOT NULL,
ticker character varying(15) NOT NULL,
mp integer NOT NULL,
settlep double precision NOT NULL,
prft integer NOT NULL,
atr20 double precision NOT NULL,
upd timestamp with time zone,
ntrds double precision
)
WITH (
OIDS=FALSE
);
CREATE TABLE st_daily2
(
CONSTRAINT st_daily2_pk PRIMARY KEY (stid, d, ticker),
CONSTRAINT st_daily2_strgs_fk FOREIGN KEY (stid)
REFERENCES strgs (stid) MATCH SIMPLE
ON UPDATE CASCADE ON DELETE CASCADE,
CONSTRAINT st_daily2_ck CHECK (stid >= 200000 AND stid < 300000)
)
INHERITS (st_daily)
WITH (
OIDS=FALSE
);
The data in this table is simulation results. Multithreaded multiple simulation engines written in c# insert data into the database using Npgsql.
psql also shows the empty row.
You'd better leave a posting at http://www.postgresql.org/support/submitbug
Some questions:
Could you show use the table
definitions and constraints for the
partions?
How did you load your data?
You get the same result when using
another tool, like psql?
The answer to your problem may very well lie in your first sentence:
I'm using postgresql 9.0 beta 4.
Why would you do that? Upgrade to a stable release. Preferably the latest point-release of the current version.
This is 9.1.4 as of today.
I got to the same point: "what in the heck is that blank value?"
No, it's not a NULL, it's a -infinity.
To filter for such a row use:
WHERE
case when mytestcolumn = '-infinity'::timestamp or
mytestcolumn = 'infinity'::timestamp
then NULL else mytestcolumn end IS NULL
instead of:
WHERE mytestcolumn IS NULL