I got these tables (Oracle):
Doctors (Doctor_ID (PK),
Doctor_Name,
DoB,
Specialization)
Doctors_At_Work (Doctor_ID (PK),
The_Date (PK),
Hour_Start (PK),
Hour_Stop,
Room)
Consultations_Intervals (Doctor_ID (PK),
The_Date (PK),
Start_Hour_Consult (PK),
Stop_Hour_Consut,
Room)
OBS: A consultation last for only 30 minutes.
My task is to create the necessary triggers (insert/update/delete) of Doctors_At_Work in order to automatically update the Consultations_Intervals table.
What I did so far:
Create sequence seq_id_doctor START with 1 INCREMENT BY 1 ORDER NOCACHE;
CREATE OR REPLACE trigger t_1
BEFORE INSERT ON Doctors_At_Work FOR EACH ROW
BEGIN
:NEW.Doctor_ID:=seq_id_doctor.NextValue;
:NEW.The_Date:=CURRENT_TIMESTAMP;
:NEW.Hour_Start:=Select Extract (Hour from CURRENT_TIMESTAMP);
END;
/
CREATE OR REPLACE trigger t_2
AFTER INSERT OR UPDATE ON Consultations_Intervals FOR EACH ROW
BEGIN
INSERT INTO Consultations_Intervals VALUES
(:NEW.Doctor_ID, :NEW.The_Date, :NEW.Hour_Start, :NEW.Hour_Start +
interval '30' minute, :NEW.Room);
END;
/
What is wrong with it? How should I solve this task? (if there are other ideas)
All you need a simple trigger on your Doctors_At_work table. See example below:
Tables:
create table Doctors_At_Work (Doctor_ID number Primary key,
The_Date date ,
Hour_Start date ,
Hour_Stop date,
Room number);
create table Consultations_Intervals (Doctor_ID number,
The_Date date ,
Start_Hour_Consult number,
Stop_Hour_Consut number,
Room number);
Trigger:
create or replace trigger t_1
before insert or update or delete on doctors_at_work
for each row
begin
insert into consultations_intervals (doctor_id,
the_date,
start_hour_consult,
stop_hour_consut,
room)
values (:new.doctor_id,
current_timestamp,
extract (minute from current_timestamp),
extract (minute from current_timestamp),
:new.room);
end;
/
Output:
SQL> Prompt "Before trigger"
"Before trigger"
SQL> select * from Doctors_At_Work;
no rows selected
SQL> select * from Consultations_Intervals;
no rows selected
SQL> create or replace trigger t_1
before insert or update or delete on doctors_at_work
2 3 for each row
4 begin
insert into consultations_intervals (doctor_id,
5 6 the_date,
7 start_hour_consult,
8 stop
.
.
.
Trigger created.
SQL> Insert into DOCTORS_AT_WORK
(DOCTOR_ID, THE_DATE, HOUR_START, HOUR_STOP, ROOM)
Values
(2, TO_DATE('12/18/2016 00:00:00', 'MM/DD/YYYY HH24:MI:SS'), TO_DATE('12/13/2016 00:00:00', 'MM/DD/YYYY HH24:MI:SS'), TO_DATE('12/14/2016 00:00:00', 'MM/DD/YYYY HH24:MI:SS'), 12);
COMMIT;
2 3 4
1 row created.
SQL>
Commit complete.
SQL> select * from Doctors_At_Work;
DOCTOR_ID THE_DATE HOUR_STAR HOUR_STOP ROOM
---------- --------- --------- --------- ----------
2 18-DEC-16 13-DEC-16 14-DEC-16 12
SQL> select * from Consultations_Intervals;
DOCTOR_ID THE_DATE START_HOUR_CONSULT STOP_HOUR_CONSUT ROOM
---------- --------- ------------------ ---------------- ----------
2 19-DEC-16 17 17 12
Related
I created a primitive table in which I can store date, time, the id of a patient and the id of an employee.
CREATE TABLE Squedule (
id INTEGER,
datee DATE NOT NULL,
timee CHAR(5),
patientId CHAR(10),
employeeId CHAR(10),
CONSTRAINT squedule_pk PRIMARY KEY (kennzahl),
CONSTRAINT squedule_fkSprstdh FOREIGN KEY (employeeId) REFERENCES Sprechstdhilfe,
CONSTRAINT squedule_fkPatientIn FOREIGN KEY (patientId) REFERENCES PatientIn);
------------------I insert the data like so
INSERT INTO squedule(datee,timee,patientid,employeeid) VALUES('02.02.3000','16:43',8137770103,3146213220);
------------------This is the trigger for the id
CREATE TRIGGER newSquedule BEFORE INSERT ON Squedule
FOR EACH ROW
BEGIN
SELECT auto_increment_pk.NEXTVAL
INTO :new.id
FROM dual;
END;
This worked just fine until I noticed that no matter what data I try to insert for the datee value it always ends up with '03.01.2022' in the table for every single insert.
I dropped all tables and created them again a few times, because a few system_sequences got created, I guess because of an Identity I use in another table. But they did not get deleted with dropping every single table.
Maybe one of them is triggering something.
What can I do to solve this problem? I am clueless...
I can't reproduce what you're saying.
I removed foreign keys from the table (as I didn't feel like creating tables you reference).
SQL> ALTER SESSION SET NLS_DATE_FORMAT = 'dd.mm.yyyy hh24:mi:ss';
Session altered.
SQL> CREATE SEQUENCE auto_increment_pk;
Sequence created.
SQL> CREATE TABLE squedule
2 (
3 id INTEGER,
4 datee DATE NOT NULL,
5 timee CHAR (5),
6 patientid CHAR (10),
7 employeeid CHAR (10),
8 CONSTRAINT squedule_pk PRIMARY KEY (id)
9 );
Table created.
SQL> CREATE OR REPLACE TRIGGER newsquedule
2 BEFORE INSERT
3 ON squedule
4 FOR EACH ROW
5 BEGIN
6 :new.id := auto_increment_pk.NEXTVAL;
7 END;
8 /
Trigger created.
Inserts:
SQL> INSERT INTO squedule (datee,
2 timee,
3 patientid,
4 employeeid)
5 VALUES ('02.02.3000',
6 '16:43',
7 8137770103,
8 3146213220);
1 row created.
But, the way you put it, I can insert anything into the timee column, including such a rubbish:
SQL> INSERT INTO squedule (datee,
2 timee,
3 patientid,
4 employeeid)
5 VALUES ('25.02.3000',
6 'xy:83',
7 8137770103,
8 3146213220);
1 row created.
Result:
SQL> SELECT * FROM squedule;
ID DATEE TIMEE PATIENTID EMPLOYEEID
---------- ------------------- ----- ---------- ----------
1 02.02.3000 00:00:00 16:43 8137770103 3146213220 --> 02.02.3000, not 03.01.2022
2 25.02.3000 00:00:00 xy:83 8137770103 3146213220 --> invalid time value
SQL>
I suggest you abandon what you're doing and use only one column whose datatype is DATE as - in Oracle - it contains both date and time component. Something like this:
SQL> DROP TABLE squedule;
Table dropped.
SQL> CREATE TABLE squedule
2 (
3 id INTEGER,
4 datee DATE NOT NULL,
5 patientid CHAR (10),
6 employeeid CHAR (10),
7 CONSTRAINT squedule_pk PRIMARY KEY (id)
8 );
Table created.
SQL> CREATE OR REPLACE TRIGGER newsquedule
2 BEFORE INSERT
3 ON squedule
4 FOR EACH ROW
5 BEGIN
6 :new.id := auto_increment_pk.NEXTVAL;
7 END;
8 /
Trigger created.
Insert:
SQL> INSERT INTO squedule (datee, patientid, employeeid)
2 VALUES (TO_DATE ('02.02.3000 14:43', 'dd.mm.yyyy hh24:mi'),
3 8137770103,
4 3146213220);
1 row created.
SQL> SELECT * FROM squedule;
ID DATEE PATIENTID EMPLOYEEID
---------- ------------------- ---------- ----------
3 02.02.3000 14:43:00 8137770103 3146213220
SQL>
I have the following code, which is working fine. If I run the procedure more than once with the same values I get a PRIMARY KEY violation, which I expect. Could the INSERT be converted into a MERGE or NOT EXISTS to avoid this issue?
The examples I saw online appear to be using literal values or an ON statement with the MERGE.
As I am a novice SQL developer any help or sample code, which reflects my requirement would be greatly appreciated.
Thanks in advance to all who answer.
ALTER SESSION SET NLS_DATE_FORMAT = 'MMDDYYYY HH24:MI:SS';
CREATE OR REPLACE TYPE nt_date IS TABLE OF DATE;
/
CREATE OR REPLACE FUNCTION generate_dates_pipelined(
p_from IN DATE,
p_to IN DATE
)
RETURN nt_date PIPELINED DETERMINISTIC
IS
v_start DATE := TRUNC(LEAST(p_from, p_to));
v_end DATE := TRUNC(GREATEST(p_from, p_to));
BEGIN
LOOP
PIPE ROW (v_start);
EXIT WHEN v_start >= v_end;
v_start := v_start + INTERVAL '1' DAY;
END LOOP;
RETURN;
END generate_dates_pipelined;
/
create table schedule_assignment(
schedule_id number(4),
schedule_date DATE,
employee_id NUMBER(6) DEFAULT 0,
constraint sa_chk check (schedule_date=trunc(schedule_date, 'dd')),
constraint sa_pk primary key (schedule_id, schedule_date)
);
CREATE OR REPLACE PROCEDURE
create_schedule_assignment (
p_schedule_id IN NUMBER,
p_start_date IN DATE,
p_end_date IN DATE
)
IS
BEGIN
INSERT INTO schedule_assignment(
schedule_id,
schedule_date
)
SELECT
p_schedule_id,
COLUMN_VALUE
FROM TABLE(generate_dates_pipelined(p_start_date, p_end_date));
END;
EXEC create_schedule_assignment (1, DATE '2021-08-21', DATE '2021-08-30');
Rewrite procedure to
SQL> CREATE OR REPLACE PROCEDURE
2 create_schedule_assignment (
3 p_schedule_id IN NUMBER,
4 p_start_date IN DATE,
5 p_end_date IN DATE
6 )
7 IS
8 BEGIN
9 merge into schedule_assignment s
10 using (select p_schedule_id as schedule_id,
11 column_value as schedule_date
12 from table(generate_dates_pipelined(p_start_date, p_end_date))
13 ) x
14 on ( x.schedule_id = s.schedule_id
15 and x.schedule_date = s.schedule_date
16 )
17 when not matched then insert (schedule_id, schedule_date)
18 values (x.schedule_id, x.schedule_date);
19 END;
20 /
Procedure created.
SQL>
Testing: initially, table is empty:
SQL> select schedule_id, min(schedule_date) mindat, max(schedule_date) maxdate, count(*)
2 from schedule_assignment group by schedule_id;
no rows selected
Run the procedure for the 1st time:
SQL> EXEC create_schedule_assignment (1, DATE '2021-08-21', DATE '2021-08-30');
PL/SQL procedure successfully completed.
Table contents:
SQL> select schedule_id, min(schedule_date) mindat, max(schedule_date) maxdate, count(*)
2 from schedule_assignment group by schedule_id;
SCHEDULE_ID MINDAT MAXDATE COUNT(*)
----------- ---------- ---------- ----------
1 21/08/2021 30/08/2021 10
Run the procedure with same parameters again:
SQL> EXEC create_schedule_assignment (1, DATE '2021-08-21', DATE '2021-08-30');
PL/SQL procedure successfully completed.
SQL> EXEC create_schedule_assignment (1, DATE '2021-08-21', DATE '2021-08-30');
PL/SQL procedure successfully completed.
SQL> EXEC create_schedule_assignment (1, DATE '2021-08-21', DATE '2021-08-30');
PL/SQL procedure successfully completed.
Result: nothing changed, no rows in table (but no error either):
SQL> select schedule_id, min(schedule_date) mindat, max(schedule_date) maxdate, count(*)
2 from schedule_assignment group by schedule_id;
SCHEDULE_ID MINDAT MAXDATE COUNT(*)
----------- ---------- ---------- ----------
1 21/08/2021 30/08/2021 10
SQL>
Run the procedure with the same SCHEDULE_ID, but different dates:
SQL> EXEC create_schedule_assignment (1, DATE '2021-08-29', DATE '2021-09-02');
PL/SQL procedure successfully completed.
SQL> select schedule_id, min(schedule_date) mindat, max(schedule_date) maxdate, count(*)
2 from schedule_assignment group by schedule_id;
SCHEDULE_ID MINDAT MAXDATE COUNT(*)
----------- ---------- ---------- ----------
1 21/08/2021 02/09/2021 13
SQL>
Right; number of rows is now increased to 13 (was 10 previously, because 31.08., 01.09. and 02.09. were added).
New SCHEDULE_ID:
SQL> EXEC create_schedule_assignment (2, DATE '2021-09-05', DATE '2021-09-07');
PL/SQL procedure successfully completed.
SQL> select schedule_id, min(schedule_date) mindat, max(schedule_date) maxdate, count(*)
2 from schedule_assignment group by schedule_id;
SCHEDULE_ID MINDAT MAXDATE COUNT(*)
----------- ---------- ---------- ----------
1 21/08/2021 02/09/2021 13
2 05/09/2021 07/09/2021 3
SQL>
Looks OK to me.
I'm trying to create a trigger that check the date that I'm inserting is greater than the one present in table for each student. The control need to be only on ID and DATE.
STUDENT_EXAMS
id_student subject mark date_exam
1 Chemistry 6 'May-05-2020'
2 Maths 7 'May-01-2020'
LEGITIMATE INSERT
insert into STUDENT_EXAMS (id_student, subject, mark, date_exam)
values (1, 'History', 8, 'May-06-2020');
insert into STUDENT_EXAMS (id_student, subject, mark, date_exam)
values (2, 'Biology', 8, 'May-05-2020');
ILLEGITIMATE INSERT
insert into STUDENT_EXAMS (id_student, subject, mark, date_exam)
values (1, 'History', 8, 'May-04-2020');
insert into STUDENT_EXAMS (id_student, subject, mark, date_exam)
values (2, 'Biology', 10, 'Apr-30-2020');
This is the trigger that I tried to create, but it's not working and I don't know how to insert also a control on each ID_STUDENT.
create or replace trigger check_date
before insert on STUDENT_EXAM
for each row
begin
if (:new.date_exam > :old.date_exam) then
insert into STUDENT_EXAM (id_student, subject, mark, date_exam)
values (:new.id_student, :new.subject, :new.mark, :new.date_exam);
end if;
end;
Perhaps this is the one that you are interested in.It prevents insertion to STUDENT_EXAM table when date_Exam for row being inserted is less than max value of date_Exam for the id.
create or replace trigger check_date
before insert on STUDENT_EXAM
for each row
DECLARE
lv_date_exam DATE;
begin
select max(date_exam) into lv_date_exam
from student_exam where id = :new.id;
if (:new.date_exam < lv_date_exam) then
raise_application_error(-20000
, 'Cannot insert record as date_exam '||:new.date_exam||' is less than max date_exam '||lv_date_exam);
end if;
end;
[EDITED by Littlefoot, to show why it will fail with the mutating table error]
Table & trigger you suggested:
SQL> create table student_exam (id number, date_exam date);
Table created.
SQL> create or replace trigger check_date
2 before insert on STUDENT_EXAM
3 for each row
4 DECLARE
5
6 lv_date_exam DATE;
7 begin
8
9 select max(date_exam) into lv_date_exam
10 from student_exam where id = :new.id;
11
12 if (:new.date_exam < lv_date_exam) then
13 raise_application_error(-20000
14 , 'Cannot insert record as date_exam '||:new.date_exam||' is less than max date_exam '||lv_date_exam);
15 end if;
16 end;
17 /
Trigger created.
Testing:
This works:
SQL> insert into student_exam (id, date_exam) values (1, sysdate);
1 row created.
But this does not:
SQL> insert into student_exam (id, date_exam)
2 select 1, sysdate - 10 from dual union all
3 select 1, sysdate + 20 from dual;
insert into student_exam (id, date_exam)
*
ERROR at line 1:
ORA-04091: table SCOTT.STUDENT_EXAM is mutating, trigger/function may not see
it
ORA-06512: at "SCOTT.CHECK_DATE", line 6
ORA-04088: error during execution of trigger 'SCOTT.CHECK_DATE'
SQL>
I have two SQL tables with the following schema:
road_test (test_ID, examiner_ID, student_ID, vin, test_date)
lessons_count (student_ID, lessons_taken)
I am looking for some way to require a student to have at least 5 lessons_taken before they can insert into the road_test table.
Is there some sort of trigger or constraint that allows for this?
Don't store count of lessons; calculate it whenever needed.
Here's what I'd suggest:
SQL> -- the final table
SQL> create table road_test
2 (test_id number, student_id number, vin number);
Table created.
SQL> -- table that shows which lessons were taken by which student
SQL> create table lesson
2 (student_id number, lesson_id number);
Table created.
SQL>
Trigger which is supposed to control whether you're allowed to insert student's record into the road_test table: count number of lessons taken and raise an error if it is too low (I set it to 3 for simplicity):
SQL> create or replace trigger trg_bi_road
2 before insert on road_test
3 for each row
4 declare
5 l_cnt number;
6 begin
7 select count(*)
8 into l_cnt
9 from lesson
10 where student_id = :new.student_id;
11 if l_cnt < 3 then
12 raise_application_error(-20001,
13 'You have to take at least 3 lessons');
14 end if;
15 end;
16 /
Trigger created.
SQL>
Testing (as I said: restricted to 3 lessons, for simplicity):
SQL> -- initial record
SQL> insert into lesson(student_id, lesson_id) values (1, 100);
1 row created.
SQL> -- can I enter that student into the ROAD_TEST table? Nope
SQL> insert into road_test (test_id, student_id, vin) values (555, 1, 123456);
insert into road_test (test_id, student_id, vin) values (555, 1, 123456)
*
ERROR at line 1:
ORA-20001: You have to take at least 3 lessons
ORA-06512: at "SCOTT.TRG_BI_ROAD", line 9
ORA-04088: error during execution of trigger 'SCOTT.TRG_BI_ROAD'
SQL> -- Let's insert 2 more lessons for the same student
SQL> insert into lesson(student_id, lesson_id) values (1, 200);
1 row created.
SQL> insert into lesson(student_id, lesson_id) values (1, 300);
1 row created.
SQL> -- New attempt for the ROAD_TEST table:
SQL> insert into road_test (test_id, student_id, vin) values (555, 1, 123456);
1 row created.
SQL> select * From lesson;
STUDENT_ID LESSON_ID
---------- ----------
1 100
1 200
1 300
SQL> select * from road_test;
TEST_ID STUDENT_ID VIN
---------- ---------- ----------
555 1 123456
SQL>
I want to connect query to connect 1st table 1st row and 2nd table first two column
means,
Table A,
ID Date Username Password
1 19/2/2016 XYZ ******
2 19/2/2016 ABC ******
Table B,
ID Date Username City
1 19/2/2016 XYZ NYC
2 19/2/2016 ABC LA
that when I insert some data in table A's 1st row then i want to check that data is available at table B's ID,DATE
Do you mean you want to enforce referential integrity between this two tables?
In this case you need a foreign key constraint
ALTER TABLE table_a
ADD CONSTRAINT reference_table_b_fk
FOREIGN KEY (id, date)
REFERENCES table_b (id, date);
If you want to just check before performing Insert option try this:
IF EXISTS (SELECT ID FROM TableB WHERE ID=1 AND Date='19/2/2016')
// Your either insert or not query
ELSE
// Your else logic will be here
so if you insert an entry into table1 only if an equivalent entry exists in table2, here's a little script to do that:
create table table1 (id number(2), date_t1 date, username varchar2(5), password varchar2(8));
create table table2 (id number(2), date_t1 date, username varchar2(5), city varchar2(8));
insert into table1 values (1, to_date('16.02.2016', 'dd.mm.yyyy'), 'XYZ', 'ABC123');
insert into table1 values (2, to_date('16.02.2016', 'dd.mm.yyyy'), 'ABC', 'XYZ123');
insert into table2 values (1, to_date('16.02.2016', 'dd.mm.yyyy'), 'XYZ', 'NYC');
insert into table2 values (2, to_date('16.02.2016', 'dd.mm.yyyy'), 'ABC', 'LA');
declare
n_id number(2);
d_date date;
v_username varchar2(5);
v_password varchar2(8);
n_check number(1);
begin
n_check := 0;
-- fill the variables with data which you want to insert:
n_id := 2;
d_date := to_date('16.02.2016', 'dd.mm.yyyy');
v_username := 'ABC';
v_password := 'CCCCC';
-- check whether an entry exists in table2:
begin
select count(1)
into n_check
from table2 t2
where t2.id = n_id
and trunc(t2.date_t1) = trunc(d_date);
exception when no_data_found then n_check := 0;
end;
-- if an entry exists in table2, then insert into table1:
if n_check <> 0 then
insert into table1 (id, date_t1, username, password)
values (n_id, d_date, v_username, v_password);
end if;
end;
/
select * from table1;
select * from table2;
delete from table1;
delete from table2;