PostgreSQL connections constraint - sql

I have table with two columns, id1 and id2.
If i have for example foo-bar respectively in these columns,I need a constraint that forbids entry bar-foo.
Thanks!

CREATE TABLE mytable(
id1 integer,
id2 integer
);
CREATE UNIQUE INDEX ON mytable(least(id1, id2), greatest(id1, id2));
This should ddo the trick:
test=> INSERT INTO mytable VALUES (1, 2);
INSERT 0 1
test=> INSERT INTO mytable VALUES (1, 3);
INSERT 0 1
test=> INSERT INTO mytable VALUES (2, 1);
ERROR: duplicate key value violates unique constraint "mytable_least_greatest_idx"
DETAIL: Key ((LEAST(id1, id2)), (GREATEST(id1, id2)))=(1, 2) already exists.

Related

SQL Constraint on more than one row

I have a table PROPERTY_RUNTIME having the columns of PROPERTY_NAME AND PROPERTY_VALUE
table PROPERTY_RUNTIME
I am adding a constraint to make the values of CNT_DISP_PREP1 and CNT_DISP_PREP2 not equal to 0 at the same time:
ALTER TABLE KLASSX.PROPERTY_RUNTIME
ADD (
CONSTRAINT CK_BOTH_LINE_CLOSE
CHECK (
(
CASE
WHEN
(
((PROPERTY_NAME = 'CNT_DISP_PREP1') AND (PROPERTY_VALUE = '1'))
OR
((PROPERTY_NAME = 'CNT_DISP_PREP2') AND (PROPERTY_VALUE = '1'))
)
THEN 1
ELSE 0
END
) = 1
)
DISABLE NOVALIDATE);
However, when I activate the constraint, and enter four combinations: 1/1, 1/0, 0/1, 0/0, all the four combinations violate the constraint, rather than that only 0/0 violates.
So I wonder if I have any logical mistake in the constraint?
Thanks in advance!
You can use a unique index to enforce that rule across multiples rows as follows:
FSITJA#db01> create table property_runtime (property_name varchar2(30), property_value varchar2(1));
Table created.
FSITJA#db01> create unique index ck_both_line_close on property_runtime (
2 case when property_name in ('CNT_DISP_PREP1', 'CNT_DISP_PREP2') and property_value = '0'
3 then 1 end)
4 ;
Index created.
FSITJA#db01> insert into property_runtime values ('CNT_DISP_PREP1', '1');
1 row created.
FSITJA#db01> insert into property_runtime values ('CNT_DISP_PREP2', '1');
1 row created.
FSITJA#db01> insert into property_runtime values ('CNT_DISP_PREP1', '0');
1 row created.
FSITJA#db01 2019-10-18 11:46:08> insert into property_runtime values ('CNT_DISP_PREP2', '0');
insert into property_runtime values ('CNT_DISP_PREP2', '0')
*
ERROR at line 1:
ORA-00001: unique constraint (FSITJA.CK_BOTH_LINE_CLOSE) violated
FSITJA#db01> insert into property_runtime values ('CNT_DISP_PREP1', '0');
insert into property_runtime values ('CNT_DISP_PREP1', '0')
*
ERROR at line 1:
ORA-00001: unique constraint (FSITJA.CK_BOTH_LINE_CLOSE) violated

Create a unique constraint with CHECK in PostgreSQL

Consider that there is the following table:
create table table_1(
id serial
PRIMARY KEY,
col1 varchar(50),
col2 varchar(50),
status varchar(1) -- A=active P=pending D=Deleted
);
now what I want is to create a unique constraint on (col1,col2) but it should not consider those with status ='D' i.e Consider there is 2 rows in the table:
INSERT INTO table_1(col1,col2,status) VALUES ('row1', 'row1', 'A');
INSERT INTO table_1(col1,col2,status) VALUES ('row2', 'row2', 'D');
Then I should NOT be able to add the following row:
INSERT INTO table_1(col1,col2,status) VALUES ('row1', 'row1', 'A');
But I SHOULD be able to add the following row:
INSERT INTO table_1(col1,col2,status) VALUES ('row2', 'row2', 'A');
You can do this with a partial unique index:
create unique index id_table1_col1_col2_status on table_1(col1, col2, status)
where status <> 'D';
Here is a SQL Fiddle that you can use to see it work.

Unique constraint on multiple columns - allow for single null

I found this: Unique constraint on multiple columns
SQL> CREATE TABLE t (id1 NUMBER, id2 NUMBER);
Table created
SQL> ALTER TABLE t ADD CONSTRAINT u_t UNIQUE (id1, id2);
Table altered
SQL> INSERT INTO t VALUES (1, NULL);
1 row inserted
SQL> INSERT INTO t VALUES (1, NULL);
INSERT INTO t VALUES (1, NULL)
ORA-00001: unique constraint (VNZ.U_T) violated
I want to create a constraint that allows to enter several (X,null) values, so that the constraint only kicks in when BOTH values that the constraint is about are not null. Is this possible?
Note that you can insert multiple (NULL,NULL), but not multiple (1,NULL). This is how indexes work in Oracle; when all columns are null, then there is no entry in the index.
So rather than building a normal index on (id1,id2) we must build a function index that makes both values null when at least one is null. We need deterministic functions for this. First DECODE to check for null. Then GREATEST, making use of it resulting in null when at least one value is null:
create unique index idx_t_unique on t
(
decode(greatest(id1,id2),null,null,id1),
decode(greatest(id1,id2),null,null,id2)
);
EDIT (after acceptance :-) I just see, you don't need deterministic functions, but can also use case constructs. Maybe that was always the case, maybe not, I don't know. However, you can also write the index as follows, if you find it more readable:
create unique index idx_t_unique on t
(
case when id1 is null or id2 is null then null else id1 end,
case when id1 is null or id2 is null then null else id2 end
);
You need a CHECK constraint in this case:
ALTER TABLE t ADD CONSTRAINT chk_t CHECK (id1 is null or id2 is null);
If you need a unique constraint behaviour you may try this:
drop table t1;
create table t1 (n number, m number);
create unique index t_inx on t1(case when n is null then null when m is null then null else n || '_' || m end);
insert into t1 values (1, null);
insert into t1 values (1, null);
insert into t1 values (null, 1);
insert into t1 values (null, 1);
insert into t1 values (1, 1);
insert into t1 values (1, 1);
insert into t1 values (1, 2);
A unique function based index

Conditional unique constraint with multiple fields in oracle db

I have this table:
XPTO_TABLE (id, obj_x, date_x, type_x, status_x)
I wanna create a unique constraint that applies to the fields (obj_x, date_x, type_x) only when status_x <> 5.
I have tried to create this one but Oracle says:
line 1: ORA-00907: missing right parenthesis
CREATE UNIQUE INDEX UN_OBJ_DT_TYPE_STATUS
ON XPTO_TABLE(
(CASE
WHEN STATUS_X <> 5
THEN
(OBJ_X,
TO_CHAR (DATE_X, 'dd/MM/yyyy'),
TYPE_X)
ELSE
NULL
END));
What's the correct syntax ?
#jamesfrj: it looks like you are trying to ensure that your table should contain only one record for which status <>5.
You can try creating a unique functional index by concatenating the columns, as given below
create table XPTO_TABLE (id number,
obj_x varchar2(20),
date_x date,
type_x varchar2(20),
status_x varchar2(20)
);
create unique index xpto_table_idx1 on XPTO_TABLE(case when status_x <>'5' THEN obj_x||date_x||type_x||STATUS_x ELSE null END);
Hope it helps
Vishad
Under Oracle 11, you can create a bunch of virtual columns that get non-NULL value only when STATUS_X is 5, and then make them unique:
CREATE TABLE XPTO_TABLE (
ID INT PRIMARY KEY,
OBJ_X INT,
DATE_X DATE,
TYPE_X VARCHAR2(50),
STATUS_X INT,
OBJ_U AS (CASE STATUS_X WHEN 5 THEN OBJ_X ELSE NULL END),
DATE_U AS (CASE STATUS_X WHEN 5 THEN DATE_X ELSE NULL END),
TYPE_U AS (CASE STATUS_X WHEN 5 THEN TYPE_X ELSE NULL END),
UNIQUE (OBJ_U, DATE_U, TYPE_U)
);
You can freely insert duplicates, as long as STATUS_X is not 5:
INSERT INTO XPTO_TABLE (ID, OBJ_X, DATE_X, TYPE_X, STATUS_X) VALUES (1, 1, '1-JAN-2014', 'foo', 4);
INSERT INTO XPTO_TABLE (ID, OBJ_X, DATE_X, TYPE_X, STATUS_X) VALUES (2, 1, '1-JAN-2014', 'foo', 4);
But trying to insert a duplicate when STATUS_X is 5 fails:
INSERT INTO XPTO_TABLE (ID, OBJ_X, DATE_X, TYPE_X, STATUS_X) VALUES (3, 1, '1-JAN-2014', 'foo', 5);
INSERT INTO XPTO_TABLE (ID, OBJ_X, DATE_X, TYPE_X, STATUS_X) VALUES (4, 1, '1-JAN-2014', 'foo', 5);
Error report -
SQL Error: ORA-00001: unique constraint (IFSAPP.SYS_C00139498) violated
00001. 00000 - "unique constraint (%s.%s) violated"
*Cause: An UPDATE or INSERT statement attempted to insert a duplicate key.
For Trusted Oracle configured in DBMS MAC mode, you may see
this message if a duplicate entry exists at a different level.
*Action: Either remove the unique restriction or do not insert the key.
Because the CREATE UNIQUE INDEX is expecting only a value you can concatenate the columns as follow
CREATE UNIQUE INDEX UN_OBJ_DT_TYPE_STATUS
ON XPTO_TABLE(
(CASE
WHEN STATUS_X <> 5
THEN OBJ_X || TO_CHAR (DATE_X, 'dd/MM/yyyy') || TYPE_X
ELSE
NULL
END));
CREATE UNIQUE INDEX UN_MYID_uniq_IDX
ON MYTABLE(
(CASE
WHEN MY_id > 1428923
THEN MY_INDEX_COLUMN
ELSE
NULL
END));
MY_INDEX_COLUMN value cannot be repeated if my_id is greater than 1428923.

PSQL unique constraint on two columns

I have a table which I use to link two more tables together in a 1 to many relationship.
TableA
id
name
TableB
id
name
LinkTable
TableA_id
TableB_id
Basically, one of TableA can have many of TableB. Very simple. The problem I have now is to create a constraint that follows the rules of this relation ship as such:
LinkTable
TableA_id TableB_id
1 1
1 2
1 3
2 1
2 2
2 3
I want to create a unique constraint which combines both the columns together as the unique value. So in the link table above, with this new constraint, I can
INSERT INTO LinkTable (TableA_id, TableB_id) VALUES (1, 4);
INSERT INTO LinkTable (TableA_id, TableB_id) VALUES (1, 5);
INSERT INTO LinkTable (TableA_id, TableB_id) VALUES (2, 6);
INSERT INTO LinkTable (TableA_id, TableB_id) VALUES (3, 1);
With out any problems
And if I try to insert:
INSERT INTO LinkTable (TableA_id, TableB_id) VALUES (1, 1);
INSERT INTO LinkTable (TableA_id, TableB_id) VALUES (1, 3);
The constraint will fire because there is already a row with 1,1 and 1,3. How would I create a postgres constraint to do this? If i set a unique constraint to both rows, then I cannot have more then one TableA_id the same and more then one TableB_id the same.
What is the solution?
have you tried setting both columns as unique?
ALTER TABLE LinkTable
ADD CONSTRAINT LinkTable_Unique UNIQUE (TableA_id, TableB_id);
create table LinkTable (
TableA_id integer REFERENCES TableA(id),
TableB_Id integer REFERENCES TableB(id),
UNIQUE(tableA_id, tableB_id)
);