Ensure Oracle row represents a unique timespan - oracle

I have to make a process in Oracle/PLSQL. I have to verify that the interval of time between start_date and end_date from a new row that I create must not intersect other start_dates and end_dates from other rows.
Now I need to check each row for that condition and if it doesn't correspond the repetitive instruction should stop and after that to display a message such as "The interval of time given is not correct".
I don't know how to make repetitive instructions in Oracle/PLSQL and I would appreciate if you would help me.
I need a loop or smth like that to verify each row in my table that the interval of time given by the date_hour_i and date_hour_e does not intersect the other intervals of time given by the rest of the rows. One more specification....the dates from each row correspond to a client and a employee that performs a haircut to the client in the given interval of time....and i want somehow not to let to introduce a new row if for the same client(or other client) and employee, the new interval of time intersects the other intervals of time with the same/other client and employee....i hope i made myself clear...

Two links for your reading pleasure:-
Time intervals with no overlaps
and
Avoiding overlap values...

why check each row? just query for the start and end times. if the result > 0, output the error message, else, insert.

i assume this will be during the BEFORE INSERT OR UPDATE trigger.
you will want to query the existing table for overlaps in the dates - but this will give a mutating trigger error.
You can get around this by using PRAGMA AUTONOMOUS_TRANSACTION to spawn a new thread.
alternately - you could save each date range in a secondary table, and use that to query against on each insert... something like the following (uncompiled)
CREATE OR REPLACE TRIGGER mytrigger
BEFORE INSERT OR UPDATE ON mytable FOR EACH ROW
DECLARE
cnt number;
BEGIN
SELECT count(*) into cnt
FROM reserved_date_range
WHERE :new.begin_date BETWEEN begin_dt and end_dt
if ( cnt > 0 ) then
raise_application_error(-20000,'Overlapping date ranges');
else
insert into reserved_date_range( begin_dt, end_dt )
values ( :new.begin_date, :new.end_date );
end if;
End;
/

Say your table is tab1 and the start date is stdate and end date is endate
also let new start date and new end date be in PLSQL variables v_stdate and v_endate.
so your insert can be something like
insert into tab1 (stdate,endate)
select v_stdate,v_endate from dual
where not exists(
select 'overlap' from tab1 t1
where v_stdate between(t1.stdate and nvl(t1.endate,v_endate)
or v_endate between(t1.stdate and nvl(t1.endate,v_endate)
)
The solution to this problem is a bit complicated because of concurrency issues. In your case you are scheduling an event (or a resource).So I suppose you have a table that holds resource (say client). Before you add another schedule (or event) for a client you should lock the particular client record like.
select client_id from Clients where client_id=p_client_id for update;
Then you can verify there are no overlaps and insert the new schedule and commit.At this point the lock will be released.Any solution that does not use a serialization object is bound to be flawed due to concurrency issues.You can do it in your PLSQL or in a After Insert trigger.But it is an absolute must to lock the actual resource record.

Related

Prevent record insert without mutating

I am trying to prevent inserts of records into a table for scheduling. If the start date of the class is between the start and end date of a previous record, and that record is the same location as the new record, then it should not be allowed.
I wrote the following trigger, which compiles, but of course mutates, and therefore has issues. I looked into compound triggers to handle this, but either it can't be done, or my understanding is bad, because I couldn't get that to work either. I would have assumed for a compound trigger that I'd want to do these things on before statement, but I only got errors.
I also considered after insert/update, but doesn't that apply after it's already inserted? It feels like that wouldn't be right...plus, same issue with mutation I believe.
The trigger I wrote is:
CREATE OR REPLACE TRIGGER PREVENT_INSERTS
before insert or update on tbl_classes
DECLARE
v_count number;
v_start TBL_CLASS_SCHED.start_date%type;
v_end TBL_CLASS_SCHED.end_date%type;
v_half TBL_CLASS_SCHED.day_is_half%type;
BEGIN
select start_date, end_date, day_is_half
into v_start, v_end, v_half
from tbl_classes
where class_id = :NEW.CLASS_ID
and location_id = :NEW.location_id;
select count(*)
into v_count
from TBL_CLASS_SCHED
where :NEW.START_DATE >= (select start_date
from TBL_CLASS_SCHED
where class_id = :NEW.CLASS_ID
and location_id = :NEW.location_id)
and :NEW.START_DATE <= (select end_date
from TBL_CLASS_SCHED
where class_id = :NEW.CLASS_ID
and location_id = :NEW.location_id);
if (v_count = 2) THEN
RAISE_APPLICATION_ERROR(-20001,'You cannot schedule more than 2 classes that are a half day at the same location');
end if;
if (v_count = 1 and :NEW.day_is_half = 1) THEN
if (v_half != 1) THEN
RAISE_APPLICATION_ERROR(-20001,'You cannot schedule a class during another class''s time period of the same type at the same location');
end if;
end if;
EXCEPTION
WHEN NO_DATA_FOUND THEN
null;
END;
end PREVENT_INSERTS ;
Perhaps it can't be done with a trigger, and I need to do it multiple ways? For now I've done it using the same logic before doing an insert or update directly, but I'd like to put it as a constraint/trigger so that it will always apply (and so I can learn about it).
There are two things you'll need to fix.
Mutating occurs because you are trying to do a SELECT in the row level part of a trigger. Check out COMPOUND triggers as a way to mitigate this. Basically you capture info at row level, and the process that info at the after statement level. Some examples of that in my video here https://youtu.be/EFj0wTfiJTw
Even with the mutating issue resolved, there is a fundamental flaw in the logic here (trigger or otherwise) due to concurrency. All you need is (say) three or four people all using this code at the same time. All of them will get "Yes, your count checks are ok" because none of them can see each others uncommitted data. Thus they all get told they can proceed and when they finally commit, you'll have multiple rows stored hence breaking the rule your tirgger (or wherever your code is run) was trying to enforce. You'll need to look an appropriate row so that you can controlling concurrent access to the table. For an UPDATE, that is easy because this means there is already some row(s) for the location/class pairing. For an INSERT, you'll need to ensure an appropriate unique constraint is in place on a parent table somewhere. Hard to say without seeing the entire model
In principle a compound trigger could be this one:
CREATE OR REPLACE TYPE CLASS_REC AS OBJECT(
CLASS_ID INTEGER,
LOCATION_ID INTEGER,
START_DATE DATE,
END_DATE DATE,
DAY_IS_HALF INTEGER
);
CREATE OR REPLACE TYPE CLASS_TYPE AS TABLE OF CLASS_REC;
CREATE OR REPLACE TRIGGER UIC_CLASSES
FOR INSERT OR UPDATE ON TBL_CLASSES
COMPOUND TRIGGER
classes CLASS_TYPE;
v_count NUMBER;
v_half TBL_CLASS_SCHED.DAY_IS_HALF%TYPE;
BEFORE STATEMENT IS
BEGIN
classes := CLASS_TYPE();
END BEFORE STATEMENT;
BEFORE EACH ROW IS
BEGIN
classes.EXTEND;
classes(classes.LAST) := CLASS_REC(:NEW.CLASS_ID, :NEW.LOCATION_ID, :NEW.START_DATE, :NEW.END_DATE, :NEW.DAY_IS_HALF);
END BEFORE EACH ROW;
AFTER STATEMENT IS
BEGIN
FOR i IN classes.FIRST..classes.LAST LOOP
SELECT COUNT(*), v_half
INTO v_count, v_half
FROM TBL_CLASSES
WHERE CLASS_ID = classes(i).CLASS_ID
AND LOCATION_ID = classes(i).LOCATION_ID
AND classes(i).START_DATE BETWEEN START_DATE AND END_DATE
GROUP BY v_half;
IF v_count = 2 THEN
RAISE_APPLICATION_ERROR(-20001,'You cannot schedule more than 2 classes that are a half day at the same location');
END IF;
IF v_count = 1 AND classes(i).DAY_IS_HALF = 1 THEN
IF v_half != 1 THEN
RAISE_APPLICATION_ERROR(-20001,'You cannot schedule a class during another class''s time period of the same type at the same location');
end if;
end if;
END LOOP;
END AFTER STATEMENT;
END;
/
But as stated by #Connor McDonald, there are several design flaws - even in a single user environment.
A user may update the DAY_IS_HALF, I don't think the procedure covers all variants. Or a user updates END_DATE and by that, the new time intersects with existing classes.
Better avoid direct insert into the table and create a PL/SQL stored procedure in which you perform all the validations you need and then, if none of the validations fail, perform the insert. And grant execute on that procedure to the applications and do not grant applications insert on that table. That is a way to have all the data-related business rules in the database and make sure that no data that violates those rules in entered into the tables, no matter by what client application, for any client application will call a stored procedure to perform insert or update and will not perform DML directly on the table.
I think the main problem is the ambiguity of the role of the table TBL_CLASS_SCHED and the lack of clear definition of the DAY_IS_HALF column (morning, afternoon ?).
If the objective is to avoid 2 reservations of the same location at the same half day, the easiest solution is to use TBL_CLASS_SCHED to enforce the constraint with (start_date, location_id) being the primary key, morning reservation having start_date truncated at 00:00 and afternoon reservation having start_date set at 12:00, and you don't need end_date in that table, since each row represents an half day reservation.
The complete solution will then need a BEFORE trigger on TBL_CLASSES for UPDATE and INSERT events to make sure start_date and end_date being clipped to match the 00:00 and 12:00 rule and an AFTER trigger on INSERT, UPDATE and DELETE where you will calculate all the half-day rows to maintain in TBL_CLASS_SCHED. (So 1 full day reservation in TBL_CLASSES, will generate 2 rows in TBL_CLASS_SCHED). Since you maintain TBL_CLASS_SCHED from triggers set on TBL_CLASSES without any need to SELECT on the later, you don't have to worry about mutating problem, and because you will constraint start_date to be either at 00:00 or 12:00, the primary key constraint will do the job for you. You may even add a unique index on (start_date, classe_id) in TBL_CLASS_SCHED to avoid a classe to be scheduled at 2 locations at the same time, if you need to.

Best practice for updating column specific triggers

Welcome Oracle pro's
In an Oracle 12 database (upgrade is already scheduled ;-)) we have a setup of different tables updating a common base table via "after update" triggers like following:
Search_Flat
ID
Field_A
Field_B
Field_C
Now table1 contains n columns where let's say 2 out of n are relevant for the Search_Flat table. As the update of table1 may only affect columns not relevant for Seach_Flat we want to add checks to the trigger. So our first approach is like following:
CREATE OR REPLACE TRIGGER tr_tbl_1_au_search
AFTER UPDATE OF
field_a,
field_b
ON schemauser.search_flat
FOR EACH ROW
BEGIN
IF :new.field_a <> :old.field_a THEN
UPDATE schemauser.search_flat SET field_a = :new.field_a WHERE id = :new.ID;
END IF;
IF :new.field_b <> :old.field_b THEN
UPDATE schemauser.search_flat SET field_b = :new.field_b WHERE id = :new.ID;
END IF;
END;
Alternatively we could also setup the trigger like following:
CREATE OR REPLACE TRIGGER tr_tbl_1_au_search
AFTER UPDATE OF
field_a,
field_b
ON schemauser.search_flat
FOR EACH ROW
BEGIN
IF :new.field_a <> :old.field_a OR :new.field_b <> :old.field_b THEN
UPDATE schemauser.search_flat
SET field_a = :new.field_a,
field_b = :new.field_b
WHERE id = :new.ID;
END IF;
END;
The question now is about the setup of the triggers themselves. Which approach is the better with respect to:
locking time of search_flat rows
overall performance of affected components (i.e., table_1, trigger and search_flat)
In production we are talking about 4 tables with 10 fields each considered in the triggers. And we have independent app servers accessing the shared database updating the 4 tables simultaneously. From time to time we detect the following error which is the reason we wan't to optimize the triggers:
ORA-02049: timeout: distributed transaction waiting for lock
Sidenote: This setup has been chosen instead of a view or materialized view due to performance reasons as the base table is used in gui with the requirement to be instantly updated and the number of records of the 4 feeding tables are too high for updating materialized view on update.
I'm looking forward to the discussion and your thoughts.
As I understand your post, you have 4 live tables (called "table1", "table2", etc.) that you want to search on, but querying from them is too slow, so you want to maintain a single, flattened table to search on instead and have triggers to keep that flattened table always up-to-date.
You want to know which of two trigger approaches is better.
I think the answer is "neither", since both are prone to deadlocks. Imagine this scenario
User 1 -
UPDATE table1
SET field_a = 500
WHERE <condition effecting 200 distinct IDs>
User 2 at about the same time -
UPDATE table1
SET field_b = 700
WHERE <condition effecting 200 distinct IDs>
Triggers start processing. You cannot control the order in which the rows are updated. Maybe it goes like this:
User 1's trigger, time index 100 ->
UPDATE search_flat SET field_a = 500 WHERE id = 90;
User 2's trigger, time index 101 ->
UPDATE search_flat SET field_b = 700 WHERE id = 91;
User 1's trigger, time index 102 ->
UPDATE search_flat SET field_a = 500 WHERE id = 91; (waits on user 2's session)
User 2's trigger, time index 103 ->
UPDATE search_flat SET field_b = 700 WHERE id = 90; (deadlock error)
User 2's original update fails and rolls back.
You have multiple concurrent processes all updating the same set of rows in search_flat with no control over the processing order. That is a recipe for deadlocks.
If you wanted to do this safely, you should consider neither of the FOR EACH ROW trigger approaches you outlines. Rather, make a compound trigger to do this.
Here's some sample code to illustrate the idea. Be sure to read the comments.
-- Aside: consider setting this at the system level if on 12.2 or later
-- alter system set temp_undo_enabled=false;
CREATE GLOBAL TEMPORARY TABLE table1_updates_gtt (
id NUMBER,
field_a VARCHAR2(80),
field_b VARCHAR2(80)
) ON COMMIT DELETE ROWS;
CREATE GLOBAL TEMPORARY TABLE table2_updates_gtt (
id NUMBER,
field_a VARCHAR2(80)
) ON COMMIT DELETE ROWS;
-- .. so on for table3 and 4.
CREATE OR REPLACE TRIGGER table1_search_maint_trg
FOR INSERT OR UPDATE OR DELETE ON table1 -- with similar compound triggers for table2, 3, 4.
COMPOUND TRIGGER
AFTER EACH ROW IS
BEGIN
-- Update the table-1 specific GTT with the changes.
CASE WHEN INSERTING OR UPDATING THEN
-- Assumes ID is immutable primary key
INSERT INTO table1_updates_gtt (id, field_a) VALUES (:new.id, :new.field_a);
WHEN DELETING THEN
INSERT INTO table1_updates_gtt (id, field_a) VALUES (:old.id, null); -- or figure out what you want to do about deletes.
END CASE;
END AFTER EACH ROW;
AFTER STATEMENT IS
BEGIN
-- Write the data from the GTT to the search_flat table.
-- NOTE: The ORDER BY in the next line is what saves us from deadlocks.
FOR r IN ( SELECT id, field_a, field_b FROM table1_updates_gtt ORDER BY id ) LOOP
-- TODO: replace with BULK processing for better performance, if DMLs can affect a lot of rows
UPDATE search_flat sf
SET sf.field_a = r.field_a,
sf.field_b = r.field_b
WHERE sf.id = r.id
AND ( sf.field_a <> r.field_a
OR (sf.field_a IS NULL AND r.field_a IS NOT NULL)
OR (sf.field_a IS NOT NULL AND r.field_a IS NULL)
OR sf.field_b <> r.field_b
OR (sf.field_b IS NULL AND r.field_b IS NOT NULL)
OR (sf.field_b IS NOT NULL AND r.field_b IS NULL)
);
END LOOP;
END AFTER STATEMENT;
END table1_search_maint_trg;
Also, as numerous commenters have pointed out, it's probably better to use a materialized view for this. If you are on 12.2 or later, real-time materialized views (aka "ENABLE ON QUERY COMPUTATION") offer a lot of promise for this sort of thing. No COMMIT overhead to your application and real-time search results. It's just that search time degrades slightly if there are a lot of recent updates to the underlying tables.

How can i insert a variable value into a column of table using trigger and sequence for oracle 10g db

Am developing an application which helps people plan there schedule.
Lets say i have a table called 'Plan_Table' in which there are columns like
id,user_name, timestamp,place,event,plan_number.
For each day, a person can insert many records depending on his activities. I want 'plan_number' column to be populated by a trigger.
Suppose an user inserts five records at a time(in a batch). I want the plan_number field to be inserted as
plan_1
plan_1
plan_1
plan_1
plan_1
if he comes up with another plan.. and does few inserts, lets say 3 this time... I want the plan_number field to be inserted as
plan_2
plan_2
plan_2
How to achieve this using trigger and sequence?
Thanks in advance.
I think you can use the combination of the before statement level trigger for that table along with the global package variables and then use them in the Row Level trigger for that table.
Hope it gives you a heads up with the above logic
var_plan_number number := 0;
begin
if :new.plan_number is null then
select max(plan_number) into var_plan_number from plan_table where timestamp < CURRENT_TIMESTAMP - 5 --*some_treshold - ie 5 seconds*
and timestamp > CURRENT_TIMESTAMP and timestamp > trunc(sysdate) and user_name = :new.user_name;
--idea is to select max plan_number for current day and increment it by 1
--treshold should be set to time, your script is able to process batch records
var_plan_number := var_plan_number + 1;
:new.plan_number := var_plan_number;
end if;
that should do the trick...
Please consider this as a pseudo code, how your trigger should look like. There is no need for sequences.
The problem lies in the definition of "at a time (in a batch)". It will be difficult to tell the trigger when one batch ends and when a new one begins. It is possible with package variables, but the most competent place is your application.
I'd create a sequence to generate the ids, but pick up the ids in your application and feed them directly to the INSERT statement:
CREATE SEQUENCE myids;
CREATE TABLE plan_table(id int, user_name varchar2(30), mytimestamp, ...);
And in your code:
SELECT myids.nextval INTO myplanid FROM DUAL;
INSERT INTO plan_table(myplanid, myuser_name, SYSTIMESTAMP, place1 ...);
INSERT INTO plan_table(myplanid, myuser_name, SYSTIMESTAMP, place2 ...);
INSERT INTO plan_table(myplanid, myuser_name, SYSTIMESTAMP, place3 ...);
COMMIT;
Thanks for the answers you provided me. You really let me think in a purely db perspective.
As a web application developer, I thought, its a much better approach to use sequence/trigger to help me out with this problem. However, I found the solution from business logic of Application itself.
I am using Hibernate ORM for managing db tables. Hence i pulled out the max value of plan number using the following pieces of code.
Session session = getSessionFactory().openSession();
session.beginTransaction();
Criteria criteria = session.createCriteria(Mwwp_Plan.class).setProjection(Projections.max("plan_number"));
Integer maxPlanNumber = (Integer) criteria.uniqueResult();
session.getTransaction().commit();
System.out.println(maxPlanNumber);
if(maxPlanNumber==null)
{
System.out.println("maxPlanNumber is null");
maxPlanNumber = 0;
}
else
{
}
System.out.println("maxPlanNumber:"+maxPlanNumber);
return maxPlanNumber;
This is inside a function which my app uses to get the max(plan_number). If there is no plan_number in the table. It will give a default of 1.
Note: Mwwp_Plan is the name of table i used in my application.
Hence I achieved what i wanted.
Thanks for your help.

oracle sql break out records

I have a table that has a StartDate and EndDate field, and also a ton of other fields. I need to break out each record by all the days between and including StartDate & EndDate into another table that looks exactly like the original except it has a CurrentDate field and 2 calculated fields. The CurrentDate field is the current date between StartDate and EndDate that I'm interating on.
My question is, since there are a ton of fields in this, is there any easy way from within my stored proc, to insert the entire row the cursor is currently on AND this 1 new column, without having to list out every single row in the insert statement? It's so tedious.
If your source and destination tables fit this profile:
Destination table columns are the same as your source table's columns, and
The new destination column is at the end
... then you could do something like this:
INSERT INTO dest_table
SELECT Source_Table.*, new_value
FROM Source_Table
WHERE Source_Table.PKValue = cursor.PKValue
If it's a case of your cursor resembling the destination table, something like this may work but note I haven't tested it:
CREATE PROCEDURE whatever IS
destRow dest_table%ROWTYPE;
CURSOR fromSourceTable IS
SELECT <your existing select list>, NULL AS new_value
FROM <the rest of your cursor query>;
BEGIN
FOR destRow IN fromSourceTable LOOP
destRow.new_value = <the split date>;
INSERT INTO dest_table VALUES destRow;
END LOOP;
END whatever;
I'm going out on a limb with the NULL AS new_value. If you have trouble try CAST(NULL AS DATE) AS new_value instead, and if you still have trouble try something like SYSDATE AS new_value. Again, this isn't tested but if you think it's promising and have trouble implementing I'd be happy to test it.
It's easy enough to densify the data in a single SQL statement. Assuming that you know a reasonable minimum and maximum range for your begin_date and end_date (I'll assume Jan 1, 2000 - Dec 31, 2020 for the moment but you can obviously adjust that)
WITH all_days AS (
SELECT date '2000-01-01' + level dt
FROM dual
CONNECT BY level <= date '2020-12-31' - date '2000-01-01'
)
SELECT <<list of colums from your table>>,
all_days.dt current_date
FROM your_table actual
JOIN all_days ON (actual.begin_date <= all_days.dt AND
actual.end_date >= all_days.dt)
If you don't want to hard-code the starting and ending dates, you can fetch them from your table as well. That just requires that you hit the table a second time which will generally be less efficient.

Writing Procedure to enforce constraints + Testing

I need to set a constraint that the user is unable to enter any records after he/she has entered 5 records in a single month. Would it be advisable that I write a trigger or procedure for that? Else is that any other ways that I can setup the constraint?
Instead of writing a trigger i have opt to write a procedure for the constraint but how do i check if the procedure is working?
Below is the procedure:
CREATE OR REPLACE PROCEDURE InsertReadingCheck
(
newReadNo In Int,
newReadValue In Int,
newReaderID In Int,
newMeterID In Int
)
AS
varRowCount Int;
BEGIN
Select Count(*) INTO varRowCount
From Reading
WHERE ReaderID = newReaderID
AND Trunc(ReadDate,'mm') = Trunc(Sysdate,'mm');
IF (varRowCount >= 5) THEN
BEGIN
DBMS_OUTPUT.PUT_LINE('*************************************************');
DBMS_OUTPUT.PUT_LINE('');
DBMS_OUTPUT.PUT_LINE(' You attempting to enter more than 5 Records ');
DBMS_OUTPUT.PUT_LINE('');
DBMS_OUTPUT.PUT_LINE('*************************************************');
ROLLBACK;
RETURN;
END;
ELSIF (varRowCount < 5) THEN
BEGIN
INSERT INTO Reading
VALUES(seqReadNo.NextVal, sysdate, newReadValue,
newReaderID, newMeterID);
COMMIT;
END;
END IF;
END;
Anyone can help me look through
This is the sort of thing that you should avoid putting in a trigger. Especially the ROLLBACK and the COMMIT. This seems extremely dangerous (and I'm not even sure whether it's possible). You might have other transactions that you wish to commit that you rollback or vice versa.
Also, by putting this in a trigger you are going to get the following error:
ORA-04091: table XXXX is mutating, trigger/function may not see it
There are ways round this but they're excessive and involve doing something funky in order to get round Oracle's insistence that you do the correct thing.
This is the perfect opportunity to use a stored procedure to insert data into your table. You can check the number of current records prior to doing the insert meaning that there is no need to do a ROLLBACK.
It depends upon your application, if insert is already present in your application many times then trigger is better option.
This is a behavior constraint. Its a matter of opinion but I would err on the side of keeping this kind of business logic OUT of your database. I would instead keep track of who added what records in the records table, and on what day/times. You can have a SP to get this information, but then your code behind should handle whether or not the user can see certain links (or functions) based on the data that's returned. Whether that means keeping the user from accessing the page(s) where they insert records, or give them read only views is up to you.
One declarative way you could solve this problem that would obey all concurrency rules is to use a separate table to keep track of number of inserts per user per month:
create table inserts_check (
ReaderID integer not null,
month date not null,
number_of_inserts integer constraint max_number_of_inserts check (number_of_inserts <= 5),
primary key (ReaderID, month)
);
Then create a trigger on the table (or all tables) for which inserts should be capped at 5:
create trigger after insert on <table>
for each row
begin
MERGE INTO inserts_check t
USING (select 5 as ReaderID, trunc(sysdate, 'MM') as month, 1 as number_of_inserts from dual) s
ON (t.ReaderID = s.ReaderID and t.month = s.month)
WHEN MATCHED THEN UPDATE SET t.number_of_inserts = t.number_of_inserts + 1
WHEN NOT MATCHED THEN INSERT (ReaderID, month, number_of_inserts)
VALUES (s.ReaderID, s.month, s.number_of_inserts);
end;
Once the user has made 5 inserts, the constraint max_number_of_inserts will fail.

Resources