Oracle check regexp if a condition is satisfied - sql

I want to add a constraint to a table in my Oracle server.
How could I do to check a phone number is 10 numbers (not letters) if a user inserts a citizen from Finland?
This is my code:
ALTER TABLE CITIZEN ADD CONSTRAINT checkPhone
CHECK (CASE Country WHEN 'Finland' THEN REGEXP_LIKE(Phone,'^[0-9]{10}$') END );
But I get the following error:
ORA-00920: invalid relational operator

Try to rewrite condition in proper way
ALTER TABLE CITIZEN ADD CONSTRAINT checkPhone
CHECK (( Country ='Finland' and REGEXP_LIKE(Phone,'^[0-9]{10}$')) or (Country!='Finland' or Country is null));

A check constraint needs to resolve to a boolean expression and a case resolves to a value so you need to compare it to something. So if you are deadset on using case then it has to do a comparison. Or update it to not use a case like #mina wrote:
Set up our test data
create table citizen (
id number,
country varchar2(100),
phone varchar2(10)
);
Table created.
insert into citizen values (10, 'Finland', '1234567890');
insert into citizen values (20, 'Ireland', 'abcdefghij');
Try to add our check constraint:
ALTER TABLE CITIZEN ADD CONSTRAINT checkPhone
CHECK (CASE Country WHEN 'Finland' THEN REGEXP_LIKE(Phone,'^[0-9]{10}$') END );
*
ERROR at line 2:
ORA-00920: invalid relational operator
But if we modify it to do a comparison:
ALTER TABLE CITIZEN ADD CONSTRAINT checkPhone
CHECK (case when country = 'Finland' and not REGEXP_LIKE(Phone,'^[0-9]{10}$') then 0 else 1 END);
Table altered.
Then we can test it:
insert into citizen values (10, 'Finland', '123456789z');
ERROR at line 1:
ORA-02290: check constraint (CHECKPHONE) violated
insert into citizen values (100, 'Finland', '1234567890');
1 row created.

Related

How to avoid space(s) while inserting data in a table in oracle

I want to avoid leading and trailing spaces in a column of a table. I tried using below check constraints but showing error - "invalid relational operator".
alter table employee add (CONSTRAINT trm_name check(trim(name)));
alter table employee add (CONSTRAINT trm_name check(trim(dept)));
Suppose, I have a table employee, and want no leading and trailing spaces on both columns(name and dept). Could anyone please help me.
Regards,
Tarak
You can use a trigger:
CREATE OR REPLACE TRIGGER EMPLOYEE_BIU
BEFORE INSERT OR UPDATE ON EMPLOYEE
FOR EACH ROW
BEGIN
:NEW.DEPT := TRIM(:NEW.DEPT);
END EMPLOYEE_BIU;
I assume, in particular, you don't want to allow non-empty strings made up entirely of spaces in those columns.
If so, I would write the constraints with decode, which handles NULL the way needed for constraints (in which an equality like NULL=something is treated the same as TRUE - unlike the same equality in WHERE clauses or join conditions, where it is treated the same as FALSE).
For example:
alter table employee
add (CONSTRAINT trm_name check(decode(name, trim(name), 1, 0) = 1));
decode returns 1 if and only if name is NULL or name is non-NULL and it has no leading or trailing spaces. It returns 0 in all other cases - if name is all spaces, then trim(name) is NULL, name is not NULL, so decode returns 0 and the constraint is FALSE.
Note that often we write conditions in the form decode(a, b, 1) = 1 (the default return value of decode is NULL, so this suffices in WHERE clauses and in join conditions). In constraints, NULL = 1 is handled the same as TRUE, so this shorthand will not work; we must give a non-NULL value in decode when a and b are not the same.
You can use the CHECK constraint as follows:
SQL> CREATE TABLE ABC (
2 NAME VARCHAR2(100) CHECK ( NAME = TRIM(NAME) ), -- use CHECK constraint
3 DEPT VARCHAR2(100) CHECK ( DEPT = TRIM(DEPT) ) -- use CHECK constraint
4 );
Table created.
SQL> -- space in name
SQL> INSERT INTO ABC VALUES (' TEJASH ','SO');
INSERT INTO ABC VALUES (' TEJASH ','SO')
*
ERROR at line 1:
ORA-02290: check constraint (TEJASH.SYS_C0014819) violated
SQL> -- space in department
SQL> INSERT INTO ABC VALUES ('TEJASH1',' SO');
INSERT INTO ABC VALUES ('TEJASH1',' SO')
*
ERROR at line 1:
ORA-02290: check constraint (TEJASH.SYS_C0014820) violated
SQL> -- valid record
SQL> INSERT INTO ABC VALUES ('TEJASH1','SO');
1 row created.
SQL>

Oracle if value to be inserted in foreign key is -1, insert null instead

I have a xml script I'm reading from to populate my database with data. One of the nodes in the xml file don't have a idDirector field (the nodes are movies) and so the xml reads a -1 as idDirector and then my stored procedure tries to insert -1 into the fk field and this makes my database returna constraint error : director -1 doesn't exist in Director table. How can I make it so it inserts null instead and make my fk field nullable?
CREATE TABLE Film (
PRIMARY KEY (idFilm),
FOREIGN KEY (idDirector) REFERENCES Director
);
Thank you
Looks like CASE to me, e.g.
insert into film (id_film, id_director)
select id_film,
case when id_director = -1 then null
else id_director
end
from ...
Will it work? Yes:
SQL> create table director (id number primary key);
Table created.
SQL> create table film (id number primary key, id_director number references director);
Table created.
SQL> insert into director values (100);
1 row created.
Inserting -1 fails:
SQL> insert into film (id, id_director) values (1, -1);
insert into film (id, id_director) values (1, -1)
*
ERROR at line 1:
ORA-02291: integrity constraint (SCOTT.SYS_C0065885) violated - parent key not
found
Inserting NULL works:
SQL> insert into film (id, id_director) values (1, null);
1 row created.
SQL>

How can I insert a row that references another postgres table via foreign key, and creates the foreign row too if it doesn't exist?

In Postgres, is there a way to atomically insert a row into a table, where one column references another table, and we look up to see if the desired row exists in the referenced table and inserts it as well if it is not?
For example, say we have a US states table and a cities table which references the states table:
CREATE TABLE states (
state_id serial primary key,
name text
);
CREATE TABLE cities (
city_id serial,
name text,
state_id int references states(state_id)
);
When I want to add the city of Austin, Texas, I want to be able to see whether Texas exists in the states table, and if so use its state_id in the new row I'm inserting in the cities table. If Texas doesn't exist in the states table, I want to create it and then use its id in the cities table.
I tried this query, but I got an error saying
ERROR: WITH clause containing a data-modifying statement must be at the top level
LINE 2: WITH inserted AS (
^
WITH state_id AS (
WITH inserted AS (
INSERT INTO states(name)
VALUES ('Texas')
ON CONFLICT DO NOTHING
RETURNING state_id),
already_there AS (
SELECT state_id FROM states
WHERE name='Texas')
SELECT * FROM inserted
UNION
SELECT * FROM already_there)
INSERT INTO cities(name, state_id)
VALUES
('Austin', (SELECT state_id FROM state_id));
Am I overlooking a simple solution?
Here is one option:
with inserted as (
insert into states(name) values ('Texas')
on conflict do nothing
returning state_id
)
insert into cities(name, state_id)
values (
'Dallas',
coalesce(
(select state_id from inserted),
(select state_id from states where name = 'Texas')
)
);
The idea is to attempt to insert in a CTE, and then, in the main insert, check if a value was inserted, else select it.
For this to work properly, you need a unique constraint on states(name):
create table states (
state_id serial primary key,
name text unique
);
Demo on DB Fiddlde
You can force the insert statement to return a value:
WITH inserted AS (
INSERT INTO states (name)
VALUES ('Texas')
ON CONFLICT (name) DO UPDATE SET name = EXCLUDED.NAME
RETURNING state_id
)
. . .
The DO UPDATE SET forces the INSERT to return something.
I notice that you don't have a unique constraint, so you also need that:
ALTER TABLE states ADD CONSTRAINT unq_state_name
UNIQUE (name);
Otherwise the ON CONFLICT doesn't have anything to work with.

Postgresql tuple constraints about NOT NULL

I am building a database in POSTGRESQL, and I would like to create NOT NULL constraints for my columns, where one and only one column would be NOT NULL.
I have two columns in my table, site_id and buffer_result_id. Only one of these columns will have values.
alter table dt.analysis_result
add constraint ar_check check (site_id NOT NULL OR buffer_result_id NOT NULL);
The above code is just some pseudo-code to show my idea. How can I achieve this function?
You could use XOR expressed as:
alter table dt.analysis_result
add constraint ar_check check (
(site_id IS NOT NULL OR buffer_result_id IS NOT NULL)
AND NOT(site_id IS NOT NULL AND buffer_result_id IS NOT NULL)
);
db<>fiddle demo
More info: Exclusive OR - Equivalences
Demo:
CREATE TABLE analysis_result(site_id INT, buffer_result_id INT);
INSERT INTO analysis_result VALUES (NULL, NULL);
-- ERROR: new row for relation "analysis_result" violates check constraint "ar_check"
INSERT INTO analysis_result VALUES (1, 2);
-- ERROR: new row for relation "analysis_result" violates check constraint "ar_check"
INSERT INTO analysis_result VALUES (NULL, 2);
INSERT INTO analysis_result VALUES (1, NULL);
SELECT * FROM analysis_result
In Postgres, you can do this with a check constraint. I think the simplest method is to count the number of not null values:
alter table dt.analysis_result add constraint ar_check
check ( (site_id is not null)::int + (buffer_result_id is not null)::int = 1
);

Check length constraint is not working in Oracle11g sqlplus

I've been trying to put length constraint , so that it would not take string whose length is more or less than 5
Create Table Statement:
create table exp(id char(10),name varchar(50));
Add Constraint Statement:
alter table exp add constraint exp1 check(length(id)=5);
Insert Statement:
insert into exp(id,name) values('10001','Abhi');
But whenever i try to insert data like the above written it shows
insert into exp(id,name) values('10001','Abhi')
*
ERROR at line 1:
ORA-02290: check constraint (VIT.EXP1) violated
Change char(10) to varchar2(10):
create table exp(id varchar2(10),name varchar(50));
A char(10) column has always a length of 10. Regardsless your insert statement. That's why you get the error.