Suppose I have a
table User(userid, usernama) and a
table Owner(ownerid, ownername) and a joined
table UserOwnerMapping(userid, ownerid, IS_MASTER)
which create a many-to-many between user and owner
The is_master default is 'N'. A user can only has one master.
How can I used a constraint to force no more than one master
for a user?
I am using Oracle.
You can achieve the constraint using a unique function-based index:
create unique index idx on userownermapping
(case when is_master='Y' then userid end);
This will only index the userid when is_master = 'Y', so forces userid to be unique when is_master = 'Y' but not otherwise:
SQL> create table UserOwnerMapping (userid integer, ownerid integer, IS_MASTER varchar2(1));
Table created.
SQL>
SQL> create unique index idx on userownermapping
2 (case when is_master='Y' then userid end);
Index created.
SQL>
SQL> insert into UserOwnerMapping (userid, ownerid, IS_MASTER) values (1, 2, 'Y');
1 row created.
SQL>
SQL> insert into UserOwnerMapping (userid, ownerid, IS_MASTER) values (1, 3, 'N');
1 row created.
SQL>
SQL> insert into UserOwnerMapping (userid, ownerid, IS_MASTER) values (1, 4, 'N');
1 row created.
SQL>
SQL> insert into UserOwnerMapping (userid, ownerid, IS_MASTER) values (1, 5, 'Y');
insert into UserOwnerMapping (userid, ownerid, IS_MASTER) values (1, 5, 'Y')
*
ERROR at line 1:
ORA-00001: unique constraint (MYSCHEMA.IDX) violated
You will need to create a pre-insert trigger on your UserOwnerMapping table.
This needs to check for the existence of the IS_MASTER = Y row for that userid/ownerid combination.
Making the userid column unique could solve the problem. Why do you need is_master?
Do you mean ownerid can be null in UserOwnerMapping?
Related
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
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.
I am working with ORACLE SQL Developer and I created a table with an id as PK, another FK, id_columnx and a column1 and inserted data into them. Then I added another 2 columns, column 2 and column 3 and when I try to insert data into these new added columns, I get the error:
ORA-01400: cannot insert NULL.
I have to mention that I don't have any triggers on the table and i DO have values in the INSERT statement. There seems to be a conflict with the PK id, but I don't understand why.
So here is the code:
create table mytable(id INT PRIMARY KEY, name varchar2(30));
insert into mytable values (1, 'Mary');
insert into mytable values (2, 'John');
insert into mytable values (3, 'Bill');
alter table mytable
add email VARCHAR2(30);
alter table mytable
add addess VARCHAR2(30);
insert into mytable (email, addess)
values ('mary#gmail.com', 'Street X');
And here is the error I get:
Error starting at line : 12 in command -
insert into mytable (email, addess)
values ('mary#gmail.com', 'Street X')
Error report -
ORA-01400: cannot insert NULL into ("ZAMFIRESCUA_49"."MYTABLE"."ID")
INSERT is for inserting new rows, UPDATE is for altering data in the current rows. As mentioned in the comments, it looks like you want to be updating Mary's row with her email/address:
UPDATE mytable
SET email = 'mary#gmail.com',
address = 'Street X'
WHERE ID = 1 --Mary's ID for example, replace with the ID of the row you want to update
You could also use a subquery to find the right ID so you don't have to always look it up:
UPDATE mytable
SET email = 'mary#gmail.com',
address = 'Street X'
WHERE ID = (SELECT ID FROM mytable WHERE name = 'Mary')
Edit:
I was thinking there were two tables while writing this answer, you could always just use the name field as your filter:
UPDATE mytable
SET email = 'mary#gmail.com',
address = 'Street X'
WHERE name = 'Mary'
You're missing a PK value in your last INSERT after the ALTER statements. Try this:
create table mytable(id INT PRIMARY KEY, name varchar2(30));
insert into mytable values (1, 'Mary');
insert into mytable values (2, 'John');
insert into mytable values (3, 'Bill');
alter table mytable add email VARCHAR2(30);
alter table mytable add addess VARCHAR2(30);
insert into mytable (id, email, addess) values (4, 'mary#gmail.com', 'Street X');
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.
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.