Timetable allocation using SQL - sql

I have these two tables in my database:
Session(startTime,endTime,date)
Allocation(startTime,endTime,date)
There are already existing sessions in the timetable, and I need to allocate a new session in a way that there are no confusions between all the sessions. I thought about something like:
ALTER TABLE allocation ADD CONSTRAINT timeC
check (startTime not between (select startTime from session)
and (select endTime from session))
The problem is that it's impossible to do so as we can't use the keyword "between" for two sets of values (same thing with the endTime).
How can I manage to add this constraint ? (I use Oracle 11g)

That can't be done via a check constraint; a trigger might help, though (as Gordon has already said). Here's an example:
SQL> create table tsession
2 (id number constraint pk_tsess primary key,
3 starttime date,
4 endtime date);
Table created.
SQL>
SQL> create table tallocation
2 (id number constraint fk_all_sess references tsession (id),
3 starttime date,
4 endtime date);
Table created.
SQL>
SQL> create or replace trigger trg_biu_all
2 before insert or update on tallocation
3 for each row
4 declare
5 l_dummy varchar2(1);
6 begin
7 select 'x'
8 into l_dummy
9 from tsession s
10 where s.id = :new.id
11 and :new.starttime between s.starttime and s.endtime;
12
13 raise_application_error(-20001, 'Can not set such a start time as it collides with TSESSION values');
14 exception
15 when no_data_found then
16 -- OK, no problem - no collision
17 null;
18 end;
19 /
Trigger created.
Now, testing:
SQL> -- Insert master record, ID = 1; it'll take whole February
SQL> insert into tsession values (1, date '2018-02-01', date '2018-02-28');
1 row created.
SQL> -- I don't want to allow this date to "jump in" between 2018-02-01 and 2018-02-28
SQL> insert into tallocation (id, starttime) values (1, date '2018-02-13');
insert into tallocation (id, starttime) values (1, date '2018-02-13')
*
ERROR at line 1:
ORA-20001: Can not set such a start time as it collides with TSESSION values
ORA-06512: at "HR.TRG_BIU_ALL", line 10
ORA-04088: error during execution of trigger 'HR.TRG_BIU_ALL'
SQL> -- This one should be OK, as it is in March
SQL> insert into tallocation (id, starttime) values (1, date '2018-03-22');
1 row created.
SQL>

Related

Integrity Constraints for date (using oracle) Current Date not working as it come up with error

I have used this trigger:
CREATE OR REPLACE TRIGGER trg_chk_future
BEFORE INSERT ON your_table
FOR EACH ROW
BEGIN
IF( :new.date_time < sysdate )
THEN
RAISE_APPLICATION_ERROR( -20001, 'date_time must be in the future' );
END IF;
END;
However the current date comes up as an error when entered such as 12/12/2021 (today date) but 13/12/21 and any date after the today date works.
Any ideas what's wrong.
If you really inserted values you wrote in the question, who-knows-what you really inserted. Because, those are strings, not date values. Therefore, Oracle tried to convert them to valid DATE datatype values and - according to what you said - failed.
It means that you should actually insert DATE values, like the following example shows (trigger is exactly the same as you made it):
SQL> create table your_table (date_time date);
Table created.
SQL> CREATE OR REPLACE TRIGGER trg_chk_future
2 BEFORE INSERT ON your_table
3 FOR EACH ROW
4 BEGIN
5 IF( :new.date_time < sysdate )
6 THEN
7 RAISE_APPLICATION_ERROR( -20001, 'date_time must be in the future' );
8 END IF;
9 END;
10 /
Trigger created.
Testing: truncated sysdate is set to midnight (which was in the past, and thus rejected):
SQL> insert into your_table values (trunc(sysdate));
insert into your_table values (trunc(sysdate))
*
ERROR at line 1:
ORA-20001: date_time must be in the future
ORA-06512: at "SCOTT.TRG_CHK_FUTURE", line 4
ORA-04088: error during execution of trigger 'SCOTT.TRG_CHK_FUTURE'
May this year, also in the past:
SQL> insert into your_table values (date '2021-05-25');
insert into your_table values (date '2021-05-25')
*
ERROR at line 1:
ORA-20001: date_time must be in the future
ORA-06512: at "SCOTT.TRG_CHK_FUTURE", line 4
ORA-04088: error during execution of trigger 'SCOTT.TRG_CHK_FUTURE'
This is in the future, so it is accepted:
SQL> insert into your_table values (date '2021-12-26');
1 row created.
SQL>
As of
the current date comes up as an error when entered such as 12/12/2021 (today date)
Maybe you meant to say
IF( :new.date_time < trunc(sysdate))
which truncates sysdate to midnight today. In that case, today's date is also accepted:
SQL> CREATE OR REPLACE TRIGGER trg_chk_future
2 BEFORE INSERT ON your_table
3 FOR EACH ROW
4 BEGIN
5 IF( :new.date_time < trunc(sysdate))
6 THEN
7 RAISE_APPLICATION_ERROR( -20001, 'date_time must be in the future' );
8 END IF;
9 END;
10 /
Trigger created.
SQL> insert into your_table values (date '2021-12-12');
1 row created.
SQL>

SQL Trigger before insert, to compare a new work date with the last one [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 4 years ago.
Improve this question
I'm creating a DB with these tables
WORK(WORK_ID, WORK_CODE, DATE_BEGIN, DATE_END)
ASSIGNMENTS( WORK_ID:WORK, WORK_CODE:WORK, EMPL_ID:EMPLOYEE)
EMPLOYEE(EMPL_ID, NAME, SRN)
I want to create a trigger to make sure that is granted to each employee a rest time of 1 day, so i need that the trigger fires before inserting a new assignment and check the date of the EMPL_ID last work and if the date of the new work is less than 1 day of distance to the new date, the trigger block the insert and raise an error. I don't know how to compare with the last work date.
How could I do something like this, thanks in advance for your help!
Something like this? I removed superfluous columns as they don't matter.
SQL> -- Tables
SQL> create table work
2 (work_id number,
3 work_code number,
4 date_begin date,
5 date_end date,
6 constraint pk_work primary key (work_id, work_code)
7 );
Table created.
SQL> create table employee
2 (empl_id number primary key);
Table created.
SQL> create table assignments
2 (work_id number,
3 work_code number,
4 empl_id number constraint fk_ae references employee (empl_id),
5 constraint fk_aw foreign key (work_id, work_code)
6 references work (work_id, work_code));
Table created.
SQL> insert into work (work_id, work_code, date_begin, date_end)
2 select 1, 500, date '2018-05-20', date '2018-05-25' from dual union
3 select 2, 500, date '2018-06-01', date '2018-06-05' from dual union
4 select 3, 500, date '2018-06-06', date '2018-06-10' from dual;
3 rows created.
SQL> insert into employee(empl_id) values (100);
1 row created.
SQL> -- Trigger
SQL> create or replace trigger trg_bi_ass
2 before insert on assignments
3 for each row
4 declare
5 l_date_begin date;
6 l_date_end date;
7 begin
8 -- Previous work
9 select max(w.date_end)
10 into l_date_end
11 from work w
12 where (w.work_id, work_code) in (select a.work_id, a.work_code
13 from assignments a
14 where a.empl_id = :new.empl_id
15 );
16 -- This work
17 select w.date_begin
18 into l_date_begin
19 from work w
20 where w.work_id = :new.work_id
21 and w.work_code = :new.work_code;
22 -- Compare dates
23 if l_date_begin - l_date_end <= 1 then
24 raise_application_error(-20001, 'Employee has to rest for at least 1 day');
25 end if;
26 end;
27 /
Trigger created.
SQL> -- Testing
SQL> -- This should be OK
SQL> insert into assignments (work_id, work_code, empl_id) values (1, 500, 100);
1 row created.
SQL> -- This should be OK, because there are several days between 2018-06-01 and 2018-05-25
SQL> insert into assignments (work_id, work_code, empl_id) values (2, 500, 100);
1 row created.
SQL> -- This should fails, because there's just 1 day between 2018-06-05 and 2016-06-06
SQL> insert into assignments (work_id, work_code, empl_id) values (3, 500, 100);
insert into assignments (work_id, work_code, empl_id) values (3, 500, 100)
*
ERROR at line 1:
ORA-20001: Employee has to rest for at least 1 day
ORA-06512: at "SCOTT.TRG_BI_ASS", line 21
ORA-04088: error during execution of trigger 'SCOTT.TRG_BI_ASS'
SQL>

Insert All with Sequence.nextVal Generates Unique Constraint violation [duplicate]

This question already has answers here:
Inserting multiple rows with sequence in Oracle
(5 answers)
Closed 4 years ago.
Why does this query throw an error:
Error report -
ORA-00001: unique constraint (ON24MASTER.LANGUAGE_CODE_PK) violated
Query
INSERT ALL
INTO LANGUAGE_CODE(language_code_id,label,language_cd,country_cd,is_elite)
VALUES(SEQ_LANGUAGE_CODE_ID.NEXTVAL,'Abkhazian','ab',NULL,'N')
INTO LANGUAGE_CODE(language_code_id,label,language_cd,country_cd,is_elite)
VALUES(SEQ_LANGUAGE_CODE_ID.NEXTVAL,'Afar','aa',NULL,'N')
INTO LANGUAGE_CODE(language_code_id,label,language_cd,country_cd,is_elite)
VALUES(SEQ_LANGUAGE_CODE_ID.NEXTVAL,'Afrikaans','af',NULL,'N')
INTO LANGUAGE_CODE(language_code_id,label,language_cd,country_cd,is_elite)
VALUES(SEQ_LANGUAGE_CODE_ID.NEXTVAL,'Akan','ak',NULL,'N')
SELECT 1 FROM DUAL;
Considering select seq_language_code_id.nextval from dual; = 198 and the latest id in db is 180;
What is wrong ? can I not use insert all with .nextVal ?
Nope, you can't do it with INSERT ALL. Have a look at what's going on:
SQL> create sequence seqa;
Sequence created.
SQL> create table test (id number);
Table created.
SQL> insert all
2 into test values (seqa.nextval)
3 into test values (seqa.nextval)
4 into test values (seqa.nextval)
5 into test values (seqa.nextval)
6 select * from dual;
4 rows created.
SQL> select * From test;
ID
----------
1
1
1
1
SQL>
See? All NEXTVALs are just the same, which - in your case - leads to a primary key violation, which means that you'll have to run separate INSERT INTO statements:
SQL> insert into test values (seqa.nextval);
1 row created.
SQL> insert into test values (seqa.nextval);
1 row created.
SQL> insert into test values (seqa.nextval);
1 row created.
SQL> select * From test;
ID
----------
1
1
1
1
2
3
4
7 rows selected.
SQL>

Oracle Constraints

I am working on a small school project using oracle database. I have created some tables and two of them are Mobile (Mobile_Number,Status_Flag) Status_Flag shows if a number is active or not and there is another table Owner_Mobile(Owner_Id FK,Mobile_ID FK). Now I should write a Constraint that prohibits the insert operation if the corresponding Status_Flag is N for the specified number. I tried to make it using sub query but this is not possible.
the constrain should be applied to OWNER_MOBILE table of course. For example if I say: INSERT INTO OWNER_MOBILE(25541,042536) the constrain should check the Mobile table and see if the Mobile 042536 is active or not . If the number is not active the insert statement should generate a error
You can use the trigger or another PL/SQL API for this but you should take into account ACID transaction principles. Let's consider the case when flag value = 0 should prevent insertion:
SQL> create table mobile (mobile_id int primary key, flag int)
2 /
SQL> create table owner_mobile(owner_id int,
2 mobile_id int references mobile(mobile_id))
3 /
SQL> insert into mobile values (1,1)
2 /
SQL> commit
2 /
SQL> create or replace trigger
2 tr_owner_mobile
3 before insert on owner_mobile
4 for each row
5 declare
6 l_flag mobile.flag%type;
7 begin
8 select flag into l_flag
9 from mobile where mobile_id = :new.mobile_id;
10
11 if l_flag = 0 then
12 raise_application_error(-20000, 'Unavalable mobile');
13 end if;
14 end;
15 /
In the code above I simply select flag and rely on the retrieved value - I don't care of ACID.
In the first transaction I update flag value but don't commit:
SQL> update mobile set flag = 0 where mobile_id = 1;
In the second transaction I insert into owner_mobile and get the success:
SQL> insert into owner_mobile values(1,1);
1 row inserted.
Next, I commit the first transaction and later - the second one. What I get then:
SQL> select * from mobile;
MOBILE_ID FLAG
---------- ----------
1 0
SQL> select * from owner_mobile;
OWNER_ID MOBILE_ID
---------- ----------
1 1
Seems this is not what I expect.
I can use select for update to prevent inconsistent behavoiur:
SQL> update mobile set flag = 1;
1 row updated.
SQL> delete from owner_mobile;
1 row deleted.
SQL> commit;
SQL> create or replace trigger
2 tr_owner_mobile
3 before insert on owner_mobile
4 for each row
5 declare
6 l_flag mobile.flag%type;
7 begin
8 select flag into l_flag
9 from mobile where mobile_id = :new.mobile_id
10 for update;
11
12 if l_flag = 0 then
13 raise_application_error(-20000, 'Unavalable mobile');
14 end if;
15 end;
16 /
Now do the same:
SQL> update mobile set flag = 0 where mobile_id = 1;
1 row updated.
Second transaction is waiting because parent row is locked:
SQL> insert into owner_mobile values(1,1);
After commit in first transaction I get in the second one:
SQL> insert into owner_mobile values(1,1);
insert into owner_mobile values(1,1)
*
error in line 1:
ORA-20000: Unavalable mobile
ORA-06512: at "SCOTT.TR_OWNER_MOBILE", line 9
ORA-04088: error in trigger 'SCOTT.TR_OWNER_MOBILE'
So whatever you do to achieve requirements you will have to consider transaction isolation.

Different timestamp for different DML queries in single transaction in oracle

I am doing an insert and delete operation on a table in single transaction.I have trigger on this table which update the log.The log has primary key as sequenceId which always gets incremented on insertion.I do delete first and then insert in the transaction.
I have two issues :
The timestamp in the log for the insert and delete is being same. Can I force it to be different.
The order of operation(insert/delete) in the log is getting reversed. It shows delete operation coming after insert operation(according to sequenceId).How can I ensure that the order is consistent in the log(insert after delete).
Example :
create table address (ID number, COUNTRY char(2));
create table address_log(SEQ_ID number, ID number, COUNTRY char(2), DML_TYPE char(1), CHANGE_DATE timestamp(6));
create sequence seq_id start with 1 increment by 100 nominvalue nomaxvalue cache 20 noorder;
create or replace trigger trg_add
before insert or delete on address
FOR EACH ROW
BEGIN
if inserting then
insert into address_log values(SEQ_ID.nextval, :new.ID, :new.COUNTRY, 'I', sysdate);
else
insert into address_log values(SEQ_ID.nextval, :old.ID, :old.COUNTRY, 'D', sysdate);
end if;
end;
insert into address values(1,'US');
insert into address values(2,'CA');
delete from address where id = 1;
insert into address values(3,'UK');
delete from address where id = 3;
if I commit last DML queries in single transaction, then I should see the same order in address_log.
What is the datatype of your timestamp column?
If you use TIMESTAMP with a large enough precision, the order should be preserved.
For example TIMESTAMP(6) (precision to the micro-second) -- which is the default precision:
SQL> CREATE TABLE t_data (ID NUMBER, d VARCHAR2(30));
Table created
SQL> CREATE TABLE t_log (ts TIMESTAMP (6), ID NUMBER, action VARCHAR2(1));
Table created
SQL> CREATE OR REPLACE TRIGGER trg
2 BEFORE INSERT ON t_data
3 FOR EACH ROW
4 BEGIN
5 INSERT INTO t_log VALUES (systimestamp, :NEW.id, 'I');
6 END;
7 /
Trigger created
SQL> INSERT INTO t_data (SELECT ROWNUM, 'x' FROM dual CONNECT BY LEVEL <= 10);
10 rows inserted
SQL> SELECT * FROM t_log ORDER BY ts;
TS ID ACTION
----------------------------- ---------- ------
19/06/13 15:47:51,686192 1 I
19/06/13 15:47:51,686481 2 I
19/06/13 15:47:51,686595 3 I
19/06/13 15:47:51,686699 4 I
19/06/13 15:47:51,686800 5 I
19/06/13 15:47:51,686901 6 I
...
In any case, if you really want to distinguish simultaneous events (concurrent inserts for instance), you can always use a sequence in addition, with the ORDER keyword to guarantee that the rows will be ordered:
CREATE SEQUENCE log_sequence ORDER
This would allow you to have a reliable sort order, even though the events took place at the same time.