Can i set values to only be allowed in a single "group" - postgresql

I have a table similar to that looks something like this:
code | group
------+------
1 | a
1 | a
2 | a
9 | b
9 | b
8 | b
Is there a way to set a constraint so that the same code can appear only in one group?
So that for example adding a row code=3; group=a would not violate the constraint, but adding a row code=1; group=b would be a violation of the constraint.

I don't believe you can do it if these fields are in the same table. It looks to me that you have a hierarchy in which group is the master level and code is detail. I see this can be solved with 3 tables:
CREATE TABLE "group" (
id "char" CONSTRAINT "group_pk" PRIMARY KEY
)
CREATE TABLE "code" (
id INTEGER CONSTRAINT "code_pk" PRIMARY KEY,
group_id "char",
FOREIGN KEY group_id REFERENCES "group" (id)
)
CREATE TABLE "data" (
id INTEGER CONSTRAINT "data_pk" PRIMARY KEY,
code_id "char",
...
FOREIGN KEY code_id REFERENCES "code" (id)
)
where data table represents the table you are talking about.

Related

idiomatic way to atomically create a table that as a record that is associated to other tables

I am coming from graph databases and postgres is still super foreign to me.
I have the following tables
CREATE TYPE runnerenum AS ENUM ('runner');
CREATE TABLE IF NOT EXISTS collections (
collectionid UUID PRIMARY KEY,
name VARCHAR(256) UNIQUE NOT NULL,
runner runnerenum NOT NULL,
runconfig JSONB
);
CREATE TABLE IF NOT EXISTS namedexprs(
namedexprid UUID PRIMARY KEY,
name VARCHAR(256) UNIQUE NOT NULL,
-- exprid UUID NOT NULL REFERENCES expressions(exprid),
collectionid UUID NOT NULL REFERENCES collections(collectionid) ON DELETE CASCADE
);
CREATE TABLE IF NOT EXISTS expressions(
exprid UUID PRIMARY KEY,
ast JSONB NOT NULL,
namedexprid UUID NOT NULL REFERENCES namedexprs(namedexprid) ON DELETE CASCADE
);
My question is what is the idiomatic way to create a collections atomically (while also creating associated expressions and namedexprs). Currently I am executing three separate queries and getting errors because of a foreign key violation.
Example of using DEFERRABLE:
CREATE TABLE parent_tbl (
parent_id integer PRIMARY KEY,
parent_val varchar UNIQUE
);
CREATE TABLE child_tbl (
child_id integer PRIMARY KEY,
parent_fk varchar REFERENCES parent_tbl (parent_val)
ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
child_val varchar
);
\d child_tbl
Table "public.child_tbl"
Column | Type | Collation | Nullable | Default
-----------+-------------------+-----------+----------+---------
child_id | integer | | not null |
parent_fk | character varying | | |
child_val | character varying | | |
Indexes:
"child_tbl_pkey" PRIMARY KEY, btree (child_id)
Foreign-key constraints:
"child_tbl_parent_fk_fkey" FOREIGN KEY (parent_fk) REFERENCES parent_tbl(parent_val) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED
BEGIN;
INSERT INTO child_tbl VALUES (1, 'dog', 'cat');
SELECT * FROM child_tbl ;
child_id | parent_fk | child_val
----------+-----------+-----------
1 | dog | cat
(1 row)
SELECT * FROM parent_tbl ;
parent_id | parent_val
-----------+------------
(0 rows)
INSERT INTO parent_tbl VALUES (1, 'dog');
SELECT * FROM parent_tbl ;
parent_id | parent_val
-----------+------------
1 | dog
COMMIT;
The key to using DEFERRABLE is that the individual data entry statements need to be bundled into the same transaction, the BEGIN;/COMMIT;. This allows DEFERRABLE INITIALLY DEFERRED to work as the constraint check is deferred until the end of the transaction. For more ways you can manipulate this see SET CONSTRAINTS.

How to leave a row in the table with value of foreign key in, if a row with a foreign key is deleted?

There is table1:
CREATE TABLE table1
(
id serial NOT NULL,
CONSTRAINT pk_table1_id PRIMARY KEY (id),
)
There is table2:
CREATE TABLE table2
(
id serial NOT NULL,
fk_1 integer,
CONSTRAINT fk_to_table1 FOREIGN KEY (fk_1)
REFERENCES table1 (id) MATCH SIMPLE
ON UPDATE CASCADE ON DELETE NO ACTION
NOT VALID
)
When I try to delete some row from table1 I got exception:
UPDATE or DELETE in the table "table1" violates the foreign key constraint "fk_to_table1" of the table "table2"
Is there some way to leave(save) a row in the table2 with same value in fk_1 (id from table1) if a row with a foreign key from table1 is deleted?
Example:
Table1:
id
1
2
3
Teble2:
id fk_1
1 | 1
2 | 1
3 | 3
4 | 2
What I want to see after delete first row from table1:
Table1:
id
2
3
Teble2:
id fk_1
1 | 1
2 | 1
3 | 3
4 | 2
You can do that using ON DELETE SET NULL:
CREATE TABLE table2
(
id int NOT NULL,
fk_1 integer,
CONSTRAINT fk_to_table1 FOREIGN KEY (fk_1)
REFERENCES table1 (id)
ON UPDATE CASCADE
ON DELETE SET NULL
);
insert into table1 values (1), (2);
insert into table2 values (1,1), (2,1), (3,2), (4,2);
Then if you run:
delete from table1
where id = 1;
Table2 will look like this:
id | fk_1
---+-----
1 |
2 |
3 | 2
4 | 2
Online example: http://rextester.com/NPUG18060
The short answer is: you cannot.
the purpose of a foreign key is to verify that whenever that attribute has a value, the value is in the referenced table (except when the value is NULL---NULL does not need to exist in the table).
Two suggestions:
Solution 1. Keep the tuple in Table2, do not delete it. Instead, add a boolean attribute that determines is the tuple has been deleted.
Specifically for your problem. Add an attribute deleted to the table.
Set if to default to false. And when the tuple is deleted
create a trigger to set this attribute to true instead. This is cumbersome
and error prone.
Solution 2 (this is my preferred solution). You can use a third table that is managed via triggers.
Every time a tuple in the table2 is added, a tuple to the table3 is
added. Table3 will only have one attribute (the one you want to have
the foreign key referenced to). Table3 will be the set of all values of the attribute that have been part of Table2. The trigger to add to Table3 should use upsert, in case the attribute being added has been seen before.
Table1 will then have the foreign key constraint pointing to Table3.

How do I create this FOREIGN KEY? - Postgres

I have 2 tables. I am trying to create a FORIEGN KEY. Here is my first table:
CREATE TABLE bills(
id serial,
name varchar(100),
payment decimal(12, 2),
payoff decimal(12, 2),
type varchar(25)
)
When I try to create a second table:
CREATE TABLE pay_dates(
id serial,
bill_id integer REFERENCES bills(id),
due_date date,
pay_date date,
paid boolean
)
I get this error:
ERROR: there is no unique constraint matching given keys for referenced table "bills".
What am I doing wrong?
The referenced column has to be unique. You should declare id as primary key:
CREATE TABLE bills(
id serial primary key,
...
Primary key (or unique) constraint must be declared explicit. Type serial ensures only that the default value is the next value from a sequence. Example:
create table test (val1 serial, val2 serial);
insert into test (val2) values (1), (2), (default), (3), (4), (default);
select * from test;
val1 | val2
------+------
1 | 1
2 | 2
3 | 1
4 | 3
5 | 4
6 | 2
(6 rows)
The parent table bills (id) must have either unique or primary key constraint.
What is causing ERROR: there is no unique constraint matching given keys for referenced table?

postgres getting data where two column references same parent id

There are 3 table in postgres database
CREATE TABLE tab_name
(
name_id integer NOT NULL,
cust_name character varying NOT NULL, -- contains names like david,jones,athur
CONSTRAINT tab_name_pkey PRIMARY KEY (name_id)
)
CREATE TABLE tab_rel
(
rel_id integer NOT NULL,
rel_desc character varying NOT NULL,-- contains relation description father son, sister brother
CONSTRAINT tab_rel_pkey PRIMARY KEY (rel_id)
)
CREATE TABLE tab_rel_map
(
rel_id integer NOT NULL,
name_id1 integer NOT NULL,
name_id2 integer NOT NULL,
CONSTRAINT tab_rel_map_name_id1_fkey FOREIGN KEY (name_id1)
REFERENCES tab_name (name_id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION,
CONSTRAINT tab_rel_map_name_id2_fkey FOREIGN KEY (name_id2)
REFERENCES tab_name (name_id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION,
CONSTRAINT tab_rel_map_rel_id_fkey FOREIGN KEY (rel_id)
REFERENCES tab_rel (rel_id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
)
I am trying to write function which take rel_id as input and should output cust_name respect to name_id1 and name_id2. Because both name_id1 and name_id2 references to same parent id I am not able to get respective name.
rel_id | relation | cust_name1 | cust_name2
------------------------------------------------
1 | Father son | David | Jones
You need to join to the same table twice - here's how you do that using aliases:
select
rm.rel_id,
r.rel_desc as relation,
n1.cust_name as cust_name1,
n2.cust_name as cust_name2
from tab_rel_map rm
join tab_rel r on r.id = rm.rel_id
left join tab_name n1 on n1.name_id = rm.name_id1
left join tab_name n2 on n2.name_id = rm.name_id2
where rm.rel_id = 1;

Postgres: complex CASCADE question - making sure you only delete unique foreign key references?

I've got some linked tables in a Postgres database, as follows:
Table "public.key"
Column | Type | Modifiers
--------+------+-----------
id | text | not null
name | text |
Referenced by:
TABLE "enumeration_value" CONSTRAINT "enumeration_value_key_id_fkey" FOREIGN KEY (key_id) REFERENCES key(id)
Table "public.enumeration_value"
Column | Type | Modifiers
--------+------+-----------
id | text | not null
key_id | text |
Foreign-key constraints:
"enumeration_value_key_id_fkey" FOREIGN KEY (key_id) REFERENCES key(id)
Referenced by:
TABLE "classification_item" CONSTRAINT "classification_item_value_id_fkey" FOREIGN KEY (value_id) REFERENCES enumeration_value(id)
Table "public.classification_item"
Column | Type | Modifiers
----------------+------+-----------
id | text | not null
transaction_id | text |
value_id | text |
Foreign-key constraints:
"classification_item_transaction_id_fkey" FOREIGN KEY (transaction_id) REFERENCES transaction(id)
"classification_item_value_id_fkey" FOREIGN KEY (value_id) REFERENCES enumeration_value(id)
I want to
delete all classification_items associated with a certain transaction
delete all enumeration_values associated with those classification_items
and finally, delete all key items associated with those enumeration_values.
The difficulty is that the key items are NOT unique to enumeration_values associated (via classification_item) with a certain transaction. They get created independently, and can exist across multiple of these transactions.
So I know how to do the second two of these steps, but not the first one:
delete from key where id in (select key_id from enumeration_value where id in (select value_id from "classification_item" where id = (select id from "transaction" where slice_id = (select id from slice where name = 'barnet'))));
# In statement above: help! How do I make sure these keys are ONLY used with these values?
delete from enumeration_value where id in (select value_id from "classification_item" where id = (select id from "transaction" where slice_id = (select id from slice where name = 'barnet')));
delete from classification_item where transaction_id in (select id from "transaction" where slice_id = (select id from slice where name = 'barnet'));
If only postgres had a CASCADE DELETE statement....
If only postgres had a CASCADE DELETE
statement....
PostgreSQL has this option for a long time, as of version 8.0 (5 years ago). Just use them.