Constraint to avoid combination of foreign keys - postgresql

I've here a problem that I couldn't find a proper solution on my researches, maybe it's because I couldn't find out the exact terms to search for it, so if this is a duplicate I will delete it.
My problem is I want to know if it is possible to avoid a combination of data between two fields. I will show the structure and the kind of data I want to avoid. It will be easier to understand.
Table_A Table_B
------------------------ -------------------------------
id integer (PK) id integer (PK)
description varchar(50) title varchar(50)
id1_fromA (FK A->id)
id2_fromA (FK A->id)
I'm trying to validate the following data on table Table_B (combination is between id1_fromA and id2_fromA)
id title id1_fromA id2_fromA
1 Some Title 1 2 --It will be permmited
2 Some other 1 2 --It is a duplicate NOT ALLOWED
3 One more 1 1 --It is equals NOT ALLOWED
4 Another 2 1 --It is same as registry id 1 so NOT ALLOWED
5 Sample data 3 2 --It is ok
With above data I can easily solve the problem for registry ID=2 with
ALTER TABLE table_B ADD CONSTRAINT UK_TO_A_FKS UNIQUE (id1_fromA, id2_fromA);
And the problem for registry ID=3 with
ALTER TABLE table_B ADD CONSTRAINT CHK_TO_A_FKS CHECK (id1_fromA != id2_fromA);
My Problem is with the registry ID=4 I want to avoid such duplicate of combination as 1,2=2,1. Is it possible to do it with a CONSTRAINT or an INDEX or an UNIQUE or I will need to create a trigger or a procedure to do so?
Thanks in advance.

You can't do this with a unique constraint, but you can do this with a unique index.
create unique index UK_TO_A_FKS
on table_b (least(id1_froma, id2_froma), greatest(id1_froma, id2_froma));

Related

How to create encoded primary key for table from two different table primary keys

I have two table Table 1 and Table 2 now I have to create thrid table whose primary key is encoded and combination of table1 and table 2 pk's. I also want to fetch the record of table3 with table 1 and table 2 on the basis of table 3 pk.
can some one help me.
solution in my mind was if I can create some thing similar to JWT toke from the table 1 and table 2 pk's and store it in table 3 pk. but the problems is how I will decode it and perform basic CRUD operation with join query between different tables

How to fill two related tables via a foreign key in a trigger function?

I have two tables which I want to fill their corresponding FOREIGN KEYs simultaneously through a TRIGGER at the time of inserting data into customers table:
CREATE TABLE customers (
customer_id SERIAL PRIMARY KEY,
sld_id integer,
customer_name varchar(35)
);
CREATE TABLE slds (
sld_id SERIAL PRIMARY KEY,
customer_id integer,
sld_code varchar(8) UNIQUE
);
ALTER TABLE customers
ADD CONSTRAINT customers_sld_id_fk
FOREIGN KEY (sld_id)
REFERENCES slds(sld_id);
ALTER TABLE slds
ADD CONSTRAINT slds_customer_id_fk
FOREIGN KEY(customer_id)
REFERENCES customers(customer_id);
I have tried to use an AFTER INSERT trigger function, but NEW.customer_id returned NULL.
Then I used BEFORE INSERT which got me the value of NEW.customer_id. However, because of the constraint and the fact that the insertion didn't take place yet the FOREIGN KEY CONSTRAINT is not fulfilled and I get an error.
I have read here that currval() and lastval() can be used but not recommended.
So I created a proxy table to store the generated values. Then, an AFTER INSERT trigger to fill in those fields back in the related tables.
I thought of using a CREATE TEMP TABLE, but found out that it only lasts for the duration of the calling function and not the connection session. Maybe I misunderstood the error message.
Is this a normal efficient practice? Namely, having a dirty table around just to use for such situations.
Or maybe there is another way to achieve this without using a proxy table?
EDITED:
SAMPLE DATA
customersTABLE:
customer_id slds_id customer_name
1 1 johns
3 2 jenn
4 3 thomas
7 4 jeff
8 5 robin
9 6 chris
10 7 larry
slds TABLE:
slds_id slds_code customer_id
1 SL747561 1
2 SL710031 3
3 SL719995 4
4 SL765369 7
5 SL738011 8
6 SL722232 9
7 SL751591 10
EDIT 2:
Forgot to mention that slds_code is generated within a trigger function:
sld_code varchar(8) := 'SL7'||to_char(floor(random() * 100000 + 1)::int, 'fm00000');

What's the proper way to block a table from further insert ? Postgresql

My table has three rows and i don't want to add any more rows.
However i want to be able to select & update on the table.
What is the best way to block further inserts ?
Assuming you have a primary key named id with the current values 1,2 and 3 you could do something like this:
alter table the_table
add constraint limit_values check (id in (1,2,3));
Now if you try to insert a new row, you either get a primary key violation (because 1,2 and 3 already exist) or you get a check constraint violation when you try to insert a different ID value that does not yet exist.

GUID. and automatic id as primary key in SQL databases

SELECT COUNT(*) FROM table_name;
My algorithm is:
check count
count+1 is the new primary key starting point
Then keep on incrementing before every insert operation
But what is this GUID? Does SQL Server provide something where it automatically generates and incremented primary key?
There are 3 options
CREATE TABLE A
(
ID INT IDENTITY(1,1) PRIMARY KEY,
... Other Columns
)
CREATE TABLE B
(
ID UNIQUEIDENTIFIER DEFAULT NEWID() PRIMARY KEY,
... Other Columns
)
CREATE TABLE C
(
ID UNIQUEIDENTIFIER DEFAULT NEWSEQUENTIALID() PRIMARY KEY,
... Other Columns
)
One reason why you might prefer C rather than B would be to reduce fragmentation if you were to use the ID as the clustered index.
I'm not sure if you're also asking about IDENTITY or not- but a GUID is a unique identifier that is (almost) guaranteed to be unique. It can be used on primary keys but isn't recommended unless you're doing offline work or planning on merging databases.
For example a "normal", IDENTITY primary key is
1 Jason
2 Jake
3 Mike
which when merging with another database which looks like
1 Lisa
2 John
3 Sam
will be tricky. You've got to re-key some columns, make sure that your FKs are in order, etc. Using GUIDs, the data looks like this, and is easy to merge:
1FB74D3F-2C84-43A6-9FB6-0EFC7092F4CE Jason
845D5184-6383-473F-A5D6-4DE98DBFBC39 Jake
8F515331-4457-49D0-A9F5-5814EE7F50BA Mike
CE789C89-E01F-4BCE-AC05-CBDF10419E78 Lisa
4D51B568-107C-4B63-9F7F-24592704118F John
7FA4ED64-7356-4013-A78A-C8CCAB329954 Sam
Note that a GUID takes a lot more space than an INT, and because of this it's recommended to use an INT as a primary key unless you absolutely need to.
create table your table
(id int indentity(1,1) primary key,
col1 varchar(10)
)
will automatically create the primary key for you.
Check GUID in the T-SQL, don't have it at hand right now.
The issue with using count , then count +1 as key, is that were you to delete a record from the middle, you would end up with a duplicate key generated.
EG:
Key Data
1 A
2 B
3 C
4 D
now delete B (count becomes 3), and insert E. This tries to make the new primary key as 4, which already exists.
Key Data
1 A
3 C
4 D <--After delete count = 3 here
4 E <--Attempted insert with key 4
You could use primary key and auto increment to make sure you don't have this issue
CREATE TABLE myTable
(
P_Id int NOT NULL AUTO_INCREMENT,
PRIMARY KEY (P_Id)
)
Or you could use GUID. How GUIDs work is by creating a 128 bit integer (represented as a 32 char hex string)
Key Data
24EC84E0-36AA-B489-0C7B-074837BCEA5D A
.
.
This results in 2^128 possible values (reaaally large), so the chances of similar values created by one computer is extremely small. Besides there are algorithms to help try and ensure that this doesn't happen. So GUID are a pretty good choice for a key as well.
As for whether you use an integer or a GUID, is usually dependent on application, policy etc.

How can I enforce a constraint only if a column is not null in Postgresql?

I would like a solution to enforce a constraint only if a column is not null. I can't seem to find a way to do this in the documentation.
create table mytable(
table_identifier_a INTEGER,
table_identifier_b INTEGER,
table_value1,...)
Do to the nature of the data, I will have identifier b and a value when the table is created. After we receive additional data, I will be able to populate identifier a. At this point I would like to ensure a unique key of (identifier_a, value1) but only if identifier_a exists.
Hopefully that makes sense, Any one have any ideas?
Ummm. Unique constraints don't prevent multiple NULL values.
CREATE TABLE mytable (
table_identifier_a INTEGER NULL,
table_identifier_b INTEGER NOT NULL,
table_value1 INTEGER NOT NULL,
UNIQUE(table_identifier_a, table_identifier_b)
);
Note that we can insert muliple NULLs into it, even when identifier_b
matches:
test=# INSERT INTO mytable values(NULL, 1, 2);
INSERT 0 1
test=# INSERT INTO mytable values(NULL, 1, 2);
INSERT 0 1
test=# select * from mytable;
table_identifier_a | table_identifier_b | table_value1
--------------------+--------------------+--------------
| 1 | 2
| 1 | 2
(2 rows)
But we can't create duplicate (a,b) pairs:
test=# update mytable set table_identifier_a = 3;
ERROR: duplicate key value violates unique constraint "mytable_table_identifier_a_key"
Of course, you do have an issue: Your table has no primary key. You
probably have a data model problem. But you didn't provide enough
details to fix that.
If it is feasible to complete the entire operation within one transaction, it is possible to change the time which postgres evaluates the constraint, i.e.:
START;
SET CONSTRAINTS <...> DEFERRED;
<SOME INSERT/UPDATE/DELETE>
COMMIT;
In this case, the constraint is evaluated at commit. See:
Postgres 7.4 Doc - Set constraints or Postgres 8.3 Doc
Actually, I would probably break this out into Two tables. You're modeling two different kinds of things. The first one is the initial version, which is only partial, and the second is the whole thing. Once the information needed to bring the first kind of thing to the second, move the row from one table to the other.
You could handle this using a trigger instead of a constraint.
If I were you, I'd split the table into two tables, and possibly create view which combines them as needed.