Insert into table requires specific condition from another table - 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>

Related

TRIGGER for validating data

I have 2 tables:
Chestionar
{
id_c pk
punctaj_max
}
Test{
id_t pk
punctaj
id_c fk
}
I want to define a trigger to validate that, before an update, the modified punctaj is between 0 and the punctaj_max from the chestionar table with that id_c.
I tried this but it doesn't work
CREATE OR REPLACE TRIGGER check_val_salary
BEFORE UPDATE of punctaj ON test
FOR EACH ROW
BEGIN
IF :new.punctaj<0 OR :new.punctaj > (Select punctaj_max from chestionar c where c.id_c=:old.id_c)
THEN
RAISE_APPLICATION_ERROR (-20508, 'Punctaj out of bounds');
END;
Any tips please?
Tables and some sample data for chestionar:
SQL> create table chestionar
2 (id_c number primary key,
3 punctaj_max number);
Table created.
SQL> insert into chestionar
2 select 1 id_c, 100 punctaj_max from dual union all
3 select 2 , 200 from dual;
2 rows created.
SQL> create table test
2 (id_t number primary key,
3 punctaj number,
4 id_c number references chestionar(id_c));
Table created.
Trigger:
SQL> create or replace trigger trg_biu_test
2 before insert or update on test
3 for each row
4 declare
5 l_punctaj_max chestionar.punctaj_max%type;
6 begin
7 select c.punctaj_max
8 into l_punctaj_max
9 from chestionar c
10 where c.id_c = :new.id_c;
11
12 if :new.punctaj not between 0 and l_punctaj_max then
13 raise_application_error(-20000, 'Punctaj should be between 0 and ' || l_punctaj_max);
14 end if;
15 exception
16 when no_data_found then
17 raise_application_error(-20001, 'Can not find boundary for that ID');
18 end;
19 /
Trigger created.
Testing:
SQL> insert into test (id_t, punctaj, id_c) values (1001, 555, 3);
insert into test (id_t, punctaj, id_c) values (1001, 555, 3)
*
ERROR at line 1:
ORA-20001: Can not find boundary for that ID
ORA-06512: at "SCOTT.TRG_BIU_TEST", line 14
ORA-04088: error during execution of trigger 'SCOTT.TRG_BIU_TEST'
SQL> insert into test (id_t, punctaj, id_c) values (1001, 555, 1);
insert into test (id_t, punctaj, id_c) values (1001, 555, 1)
*
ERROR at line 1:
ORA-20000: Punctaj should be between 0 and 100
ORA-06512: at "SCOTT.TRG_BIU_TEST", line 10
ORA-04088: error during execution of trigger 'SCOTT.TRG_BIU_TEST'
SQL> insert into test (id_t, punctaj, id_c) values (1001, 55, 1);
1 row created.
SQL>

No matter what data I insert into my table, the date stays the same at 03.01.2022. How can I fix this?

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>

SQL - Trigger to check insert of date on each student

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>

Multiple insert SQL oracle

How do you do multiple insert with SQL in Oracle 12c when you have an identity column?
INSERT ALL
INTO Table1 (Column2) Values (1)
INTO Table1 (Column2) Values (2)
SELECT * FROM dual;
where Table1 has column1 as an identity, will set the identity column to have the same value which violates the primary key constraint.
CREATE TABLE Table1 (
Table1Id NUMBER GENERATED ALWAYS AS IDENTITY,
column2 VARCHAR2(255),
column3 NUMBER,
PRIMARY KEY (Table1Id)
);
INSERT ALL
INTO Table1 (column2, column3) VALUES ('a', '1')
INTO Table1 (column2, column3) VALUES ('b', '2')
SELECT * FROM dual;
--SQL Error: ORA-00001: unique constraint violated
What am I doing wrong with this?
EDIT Added two test cases, and a possible workaround.
Though Insert statement and insert all statement are practically the same conventional insert statement. But when it comes to sequences, they work differently.
Test case 1 : Identity columns
SQL> DROP TABLE table1 PURGE;
Table dropped.
SQL>
SQL> CREATE TABLE Table1 (
2 Table1Id NUMBER GENERATED ALWAYS AS IDENTITY,
3 column3 NUMBER,
4 PRIMARY KEY (Table1Id)
5 );
Table created.
SQL>
SQL> INSERT ALL
2 INTO Table1 (column3) VALUES ('1')
3 INTO Table1 (column3) VALUES ('2')
4 SELECT * FROM dual;
INSERT ALL
*
ERROR at line 1:
ORA-00001: unique constraint (LALIT.SYS_C0010439) violated
SQL>
Let's see what's actually happening under the hood -
SQL> CREATE TABLE Table1 (
2 Table1Id NUMBER GENERATED ALWAYS AS IDENTITY,
3 column3 NUMBER,
4 CONSTRAINT A UNIQUE (Table1Id)
5 );
Table created.
SQL> INSERT ALL
2 INTO Table1 (column3) VALUES (1)
3 INTO Table1 (column3) VALUES (2)
4 SELECT * FROM dual;
INSERT ALL
*
ERROR at line 1:
ORA-00001: unique constraint (LALIT.A) violated
SQL> SELECT * FROM table1;
no rows selected
SQL> ALTER TABLE table1
2 DISABLE CONSTRAINT a;
Table altered.
SQL> INSERT ALL
2 INTO Table1 (column3) VALUES (1)
3 INTO Table1 (column3) VALUES (2)
4 SELECT * FROM dual;
2 rows created.
SQL> SELECT * FROM table1;
TABLE1ID COLUMN3
---------- ----------
2 1
2 2
SQL>
So, the sequence progressed to nextval however there was an unique constraint violation the first time we did an Insert All. Next, we disabled the unique constraint, and the subsequent Insert All reveals that the sequence did not progress to nextval, rather it attempted to insert duplicate keys.
Though the issue doesn't occur with a INSERT-INTO-SELECT statement.
SQL> INSERT INTO table1(column3) SELECT LEVEL FROM dual CONNECT BY LEVEL <=5;
5 rows created.
SQL>
SQL> SELECT * FROM table1;
TABLE1ID COLUMN3
---------- ----------
2 1
3 2
4 3
5 4
6 5
SQL>
Surprisingly, as per the metadata, the sequence is supposed to proceed to nextval automatically, however it doesn't happen with an Insert All statement.
SQL> SELECT COLUMN_NAME,
2 IDENTITY_COLUMN,
3 DATA_DEFAULT
4 FROM user_tab_cols
5 WHERE table_name ='TABLE1'
6 AND IDENTITY_COLUMN='YES';
COLUMN_NAME IDENTITY_COLUMN DATA_DEFAULT
--------------- --------------- ------------------------------
TABLE1ID YES "LALIT"."ISEQ$$_94458".nextval
SQL>
Test Case 2 : Using a sequence explicitly
The INSERT ALL would work the same way whether an identity column is used or an explicit sequence is used.
SQL> DROP SEQUENCE s;
Sequence dropped.
SQL>
SQL> CREATE SEQUENCE s;
Sequence created.
SQL>
SQL> DROP TABLE t PURGE;
Table dropped.
SQL>
SQL> CREATE TABLE t (
2 ID NUMBER,
3 text VARCHAR2(50),
4 CONSTRAINT id_pk PRIMARY KEY (ID)
5 );
Table created.
SQL>
SQL> INSERT ALL
2 INTO t VALUES (s.nextval, 'a')
3 INTO t VALUES (s.nextval, 'b')
4 INTO t VALUES (s.nextval, 'c')
5 INTO t VALUES (s.nextval, 'd')
6 SELECT * FROM dual;
INSERT ALL
*
ERROR at line 1:
ORA-00001: unique constraint (LALIT.ID_PK) violated
SQL>
SQL> SELECT * FROM T;
no rows selected
SQL>
SQL> ALTER TABLE t
2 DISABLE CONSTRAINT id_pk;
Table altered.
SQL> INSERT ALL
2 INTO t VALUES (s.nextval, 'a')
3 INTO t VALUES (s.nextval, 'b')
4 INTO t VALUES (s.nextval, 'c')
5 INTO t VALUES (s.nextval, 'd')
6 SELECT * FROM dual;
4 rows created.
SQL> SELECT * FROM T;
ID TEXT
---------- ----------------------------------------
2 a
2 b
2 c
2 d
SQL>
Possible workaround - Using a ROW LEVEL trigger
SQL> CREATE OR REPLACE TRIGGER t_trg
2 BEFORE INSERT ON t
3 FOR EACH ROW
4 WHEN (new.id IS NULL)
5 BEGIN
6 SELECT s.NEXTVAL
7 INTO :new.id
8 FROM dual;
9 END;
10 /
Trigger created.
SQL> truncate table t;
Table truncated.
SQL> INSERT ALL
2 INTO t (text) VALUES ('a')
3 INTO t (text) VALUES ('b')
4 INTO t (text) VALUES ('c')
5 INTO t (text) VALUES ('d')
6 SELECT * FROM dual;
4 rows created.
SQL> SELECT * FROM t;
ID TEXT
---------- -------------------------
3 a
4 b
5 c
6 d
SQL>
Here's a workaround using the UNION ALL method instead of the INSERT ALL method. For some reason the data must be wrapped in a select * from (...) or it will generate the error ORA-01400: cannot insert NULL into ("JHELLER"."TABLE1"."TABLE1ID").
insert into table1(column2, column3)
select *
from
(
select 'a', '1' from dual union all
select 'b', '2' from dual
);

Query to output values which are not present in table

Need help in Oracle query
Requirement:
I have 5 rows in a table lets say ID = 1, 2, 3, 4, 5
Requirement is as such that user may pass any value as input and if that value is not there in table then query should return me the value which is not present.
Ex:
1. If user passes 9 then Oracle query should return the output as 9
2. If user passes 1,2,10 then Oracle query should return the output as 10
as 9 and 10 in above example are not in table.
I am using following query but not getting result.
SELECT ID
FROM TABLE_NAME WHERE ID NOT IN
(SELECT ID
FROM TABLE_NAME where ID NOT in (1,2,10))
create table z (id number);
Table created.
SQL> insert into z values (1);
1 row created.
SQL> insert into z values (2);
1 row created.
SQL> insert into z values (3);
1 row created.
SQL> insert into z values (4);
1 row created.
SQL> insert into z values (5);
1 row created.
SQL> select 10 id from dual
2 minus
3 select id from z;
ID
----------
10
You could use a nested table as input:
SQL> CREATE TABLE table_name (ID NUMBER NOT NULL);
Table created
SQL> INSERT INTO table_name (SELECT ROWNUM FROM dual CONNECT BY LEVEL <= 5);
5 rows inserted
SQL> CREATE TYPE tab_number AS TABLE OF NUMBER;
2 /
Type created
SQL> SELECT *
2 FROM TABLE(tab_number(1,2,10)) x
3 WHERE x.column_value NOT IN (SELECT ID FROM table_name);
COLUMN_VALUE
------------
10