Postgres - Configure Unique Constraint on values in 2 columns [duplicate] - postgresql

I'm trying to create a table that would enforce a unique combination of two columns of the same type - in both directions. E.g. this would be illegal:
col1 col2
1 2
2 1
I have come up with this, but it doesn't work:
database=> \d+ friend;
Table "public.friend"
Column | Type | Modifiers | Storage | Stats target | Description
--------------+--------------------------+-----------+----------+--------------+-------------
user_id_from | text | not null | extended | |
user_id_to | text | not null | extended | |
status | text | not null | extended | |
sent | timestamp with time zone | not null | plain | |
updated | timestamp with time zone | | plain | |
Indexes:
"friend_pkey" PRIMARY KEY, btree (user_id_from, user_id_to)
"friend_user_id_to_user_id_from_key" UNIQUE CONSTRAINT, btree (user_id_to, user_id_from)
Foreign-key constraints:
"friend_status_fkey" FOREIGN KEY (status) REFERENCES friend_status(name)
"friend_user_id_from_fkey" FOREIGN KEY (user_id_from) REFERENCES user_account(login)
"friend_user_id_to_fkey" FOREIGN KEY (user_id_to) REFERENCES user_account(login)
Has OIDs: no
Is it possible to write this without triggers or any advanced magic, using constraints only?

A variation on Neil's solution which doesn't need an extension is:
create table friendz (
from_id int,
to_id int
);
create unique index ifriendz on friendz(greatest(from_id,to_id), least(from_id,to_id));
Neil's solution lets you use an arbitrary number of columns though.
We're both relying on using expressions to build the index which is documented
https://www.postgresql.org/docs/current/indexes-expressional.html

Do you consider the intarray extension to be magic?
You'd need to use int keys for the users instead of text though...
Here's a possible solution:
create extension intarray;
create table friendz (
from_id int,
to_id int
);
create unique index on friendz ( sort( array[from_id, to_id ] ) );
insert into friendz values (1,2); -- good
insert into friendz values (2,1); -- bad
http://sqlfiddle.com/#!15/c84b7/1

Related

pgr_dijkstra returns an empty set

I'm trying to write a function that is able to find the shortest way between two points using pgr_dijkstra function. I'm following this guide. With data provided in the guide everything works fine. But when I try to apply the same steps (build a topology using pgr_createTopology and then test it with pgr_dijkstra) to another data set, pgr_dijkstra returns an empty result. I've also noticed that the guide's data set has a LineString geometry column, while I have a MultiLineString geometry column. What could be the reason?
My table's structure:
Table "public.roads"
Column | Type | Collation | Nullable | Default
--------+--------------------------------+-----------+----------+------------------------------------
id | integer | | not null | nextval('roads_gid_seq'::regclass)
geom | geometry(MultiLineString,4326) | | |
source | integer | | |
target | integer | | |
Indexes:
"roads_pkey" PRIMARY KEY, btree (id)
"roads_geom_idx" gist (geom)
"roads_source_idx" btree (source)
"roads_target_idx" btree (target)
Topology creation query:
SELECT pgr_createTopology('roads', 0.00001, 'geom', 'id');
Shortest way test:
SELECT seq, node, edge, cost as cost, agg_cost, geom
FROM pgr_dijkstra(
'SELECT id, source, target, st_length(geom, true) AS cost FROM roads',
-- Some random points
1, 200
) AS pt
JOIN roads rd ON pt.edge = rd.id;
The problem was actually related to geometry data types. The function doesn't work properly with MultiLineString, though it doesn't produce any errors. So, I've converted MultiLineString to LineString and now everything seems to be OK.

How to fill a nullable integer column and convert it into a serial primary key in Postgresql?

My table contains an integer column (gid) which is nullable:
gid | value
-------------
0 |  a
| b
1 | c
2 | d
| e
Now I would like to change the gid column into a SERIAL primary key column. That means filling up the empty slots with new integers. The existing integers must remain in place. So the result should look like:
gid | value
-------------
0 |  a
3 | b
1 | c
2 | d
4 | e
I just can't figure out the right SQL command for doing the transformation. Code sample would be appreciated...
A serial is "just" a column that takes it default value from a sequence.
Assuming your table is named n1000 then the following will do what you want.
The first thing you need to do is to create that sequence:
create sequence n1000_gid_seq;
Then you need to make that the "default" for the column:
alter table n1000 alter column gid set default nextval('n1000_gid_seq');
To truly create a "serial" you also need to tell the sequence that it is associated with the column:
alter sequence n1000_gid_seq owned by n1000.gid;
Then you need to advance the sequence so that the next value doesn't collide with the existing values:
select setval('n1000_gid_seq', (select max(gid) from n1000), true);
And finally you need to update the missing values in the table:
update n1000
set gid = nextval('n1000_gid_seq')
where gid is null;
Once this is done, you can define the column as the PK:
alter table n1000
add constraint pk_n1000
primary key (gid);
And of course if you have turned off autocommit you need to commit all this.

Postgresql enforce unique two-way combination of columns

I'm trying to create a table that would enforce a unique combination of two columns of the same type - in both directions. E.g. this would be illegal:
col1 col2
1 2
2 1
I have come up with this, but it doesn't work:
database=> \d+ friend;
Table "public.friend"
Column | Type | Modifiers | Storage | Stats target | Description
--------------+--------------------------+-----------+----------+--------------+-------------
user_id_from | text | not null | extended | |
user_id_to | text | not null | extended | |
status | text | not null | extended | |
sent | timestamp with time zone | not null | plain | |
updated | timestamp with time zone | | plain | |
Indexes:
"friend_pkey" PRIMARY KEY, btree (user_id_from, user_id_to)
"friend_user_id_to_user_id_from_key" UNIQUE CONSTRAINT, btree (user_id_to, user_id_from)
Foreign-key constraints:
"friend_status_fkey" FOREIGN KEY (status) REFERENCES friend_status(name)
"friend_user_id_from_fkey" FOREIGN KEY (user_id_from) REFERENCES user_account(login)
"friend_user_id_to_fkey" FOREIGN KEY (user_id_to) REFERENCES user_account(login)
Has OIDs: no
Is it possible to write this without triggers or any advanced magic, using constraints only?
A variation on Neil's solution which doesn't need an extension is:
create table friendz (
from_id int,
to_id int
);
create unique index ifriendz on friendz(greatest(from_id,to_id), least(from_id,to_id));
Neil's solution lets you use an arbitrary number of columns though.
We're both relying on using expressions to build the index which is documented
https://www.postgresql.org/docs/current/indexes-expressional.html
Do you consider the intarray extension to be magic?
You'd need to use int keys for the users instead of text though...
Here's a possible solution:
create extension intarray;
create table friendz (
from_id int,
to_id int
);
create unique index on friendz ( sort( array[from_id, to_id ] ) );
insert into friendz values (1,2); -- good
insert into friendz values (2,1); -- bad
http://sqlfiddle.com/#!15/c84b7/1

Partitioning in PostgreSQL when partitioned table is referenced

My PostgreSQL database has table with entities which can be active and inactive - it's determined by isActive column value. Inactive entities are accessed very rarely, and, as database grows, "inactive to active" rate becomes very high for the database. So I expect partitioning based on simple isActive check to bring huge performance outcome.
The problem is, the table is referenced by foreign key constraint from many other tables. As specified in the last bullet of Caveats section of PostgreSQL Inheritance doc, there is no good workaround for this case.
So, is it true that currently partitioning in PostgreSQL is only suitable for the simple cases when the table partitioned is not referenced from anywhere?
Are there any other ways to go and optimize performance of queries to the table I described above? I'm pretty sure my use case is common and there should be good solution for that.
Example of queries to create the tables:
CREATE TABLE resources
(
id uuid NOT NULL,
isActive integer NOT NULL, -- 0 means false, anything else is true, I intentionally do not use boolean type
PRIMARY KEY (id)
);
CREATE TABLE resource_attributes
(
id uuid NOT NULL,
resourceId uuid NOT NULL,
name character varying(128) NOT NULL,
value character varying(1024) DEFAULT NULL,
PRIMARY KEY (id),
CONSTRAINT fk_resource_attributes_resourceid_resources_id FOREIGN KEY (resourceId) REFERENCES resources (id)
);
In this case, I'd like to partition resources table.
If the inactive to active ratio is very high a partial index is a good choice
create index index_name on resources (isActive) where isActive = 1
The only known workaround (i can think of) to create a foreign key for a table which has multiple child tables is to create another table to hold just the primary keys (but all of them, maintained by triggers) and point all foreign key references to it.
like:
+-----------+ +----------------+ +---------------------+
| resources | | resource_uuids | | resource_part_n |
+===========+ 0 1 +================+ 1 0 +=====================+
| id | --> | id | <-- | (id from resources) |
+-----------+ +----------------+ +---------------------+
| ... | ↑ 1 | CHECK(...) |
+-----------+ +--------+ +---------------------+
| | INHERITS(resources) |
+---------------------+ | +---------------------+
| resource_attributes | |
+---------------------+ |
| resourceId | --+ *
+---------------------+
| ... |
+---------------------+
But you still can't partition that table (resource_uuids), so i don't think partitioning will help you in this case.

Perl: Is there a DBI function similar to statistics_info, to retrieve FK references and constraints?

When performing a \d+ on a table within Postgres, it lists the table schema along with the indexes, as well as other tables that reference it as a FK. Example:
Table "public.foo_table"
Column | Type | Modifiers | Storage | Description
------------+------+---------------+----------+-------------
id | text | | extended |
foo | text | | extended |
bar | text | | extended |
Indexes:
"foo_table_id_idx" btree (id)
"foo_table_foobar_idx" btree (foo,bar)
Foreign-key constraints:
"foo_table_bar_fk" FOREIGN KEY (bar) REFERENCES public.bar_table(id)
Referenced by:
TABLE "public.bar_table" CONSTRAINT "bar_table_foo_fk" FOREIGN KEY (foo) REFERENCES public.foo_table(foo)
Has OIDs: no
You can do something $dbh->statistics_info(...) to retrieve the index information. Is there something similar to retrieve the FK info (references and referenced-by)?
It seems like my next option is to either issue a ->do() command, or query the system tables.
What I've found so far:
$dbh->foreign_key_info( pk_cat, pk_schema, pk_tbl
, fk_cat, fk_schema, fk_tbl );
# FK References
$dbh->foreign_key_info( undef , undef , undef
, undef , undef , $table_name );
# FK Referenced By
$dbh->foreign_key_info( undef , undef , $table_name
, undef , undef , undef );
## Putting the schema/catalog info only ensures you are hitting the intended
## table. If you have dupicate tables, or your table is not in the public
## schema it's probably a good idea to include the schema.
## Catalog is generally unnecessary for Postgres
If you run psql with the -E option it will show all the queries it runs to respond to \d (and other) metadata requests. It's pretty easy to copy/paste from that to get the queries you want.