Oracle and mutating table with simple exercise - sql

I'm in trouble with the implementation of a trigger.
Assuming that I have two types:
CREATE TYPE customer_t AS OBJECT(
code INTEGER,
name VARCHAR(20),
surname VARCHAR(20),
age INTEGER);
and the type
CREATE TYPE ticket_t AS OBJECT (
price INTEGER,
cust REF customer_t
)
And then I have the associate tables:
CREATE TABLE customers OF TYPE customer_t
CREATE TABLE tickets OF TYPE ticket_t
I have to do an exercise so I have to create a trigger for ensure that a customer won't buy more than 10 tickets but, if I use command like "select count(*)" I get an error because I can't access to mutating table.
Please can anyone help me with this trigger?
EDIT:
I populated the tables as follows:
INSERT INTO custs (code, name, surname, age) values (123, 'Paolo', 'Past', 32);
and repeating the following operation ten times:
INSERT INTO tickets (price, cust) values
(4, (SELECT * FROM (SELECT REF(T) FROM custs T WHERE name = 'Paolo' AND surname = 'Past') WHERE rownum < 2))
The trigger implemented is:
create or replace
trigger check_num_ticket after insert on tickets
for each row
declare
num_ticket number;
begin
SELECT count(*) INTO num_ticket FROM tickets WHERE :new.cust = cust;
if (num_ticket >= 10) then
raise_application_error('-20099', 'no ticket available');
end if;
end;
And I get this error:
A trigger (or a user defined plsql function that is referenced in
this statement) attempted to look at (or modify) a table that was
in the middle of being modified by the statement which fired it.

You are getting the mutating table error, because you are inserting in the same table where you want to get the row count for. Imagine your insert statement inserts two rows. There is no rule which row to insert first and which last, but your trigger fires on one inserted row and wants to know how many rows are already in the table. The DBMS tells you this is undefined, as the table is currently mutating.
You need an after statement trigger instead of a before row trigger. So when the insert statement's inserts are done, you look at the table to see whether there are suddenly customers with too many rows in it.
(A great alternative is a compound trigger. It combines row and statement triggers. So in the after row section you'd remember the customers in some array/collection and in the after statement section you'd look up the table for only the remembered customers.)

Related

From keyword not found where expected error in oracle

Select firstname as name, time as asof, salary as bal into temp employee
from people.person p
where p.id =1;
Need to create a temporary table employee by inserting values from already created table person which belongs to people database but getting error from keyword not found where expected
You'd then use CTAS (Create Table As Select), not an invalid INTO clause; it is used for different purposes.
create table temp_employee as
select firstname as name,
time as asof,
salary as bal
from people.person p
where p.id = 1;
Based on comment you posted, there are several options you might want to consider.
One is to create a permanent table (just like the above example shows). If you'll reuse it, then - in your procedure - first delete its contents (or truncate the table as it is way faster), and then re-populate it:
delete from temp_employee;
-- or truncate table temp_employee;
insert into temp_employee
select firstname, time, salary from people.person
where id = 1;
Another option is to create a true temporary table, e.g.
create global temporary table temp_employee
(name varchar2(30),
time date,
bal number
)
on commit preserve rows;
Whenever you need data in it, just insert it:
insert into temp_employee (name, time, bal)
select firstname as name,
time as asof,
salary as bal
from people.person p
where p.id = 1;
Doing so, its contents will be visible only to you (and nobody else), while table's contents will be kept during the transaction or session (it depends on how you created it - see the on commit preserve/delete rows clause).
What you should not do is to create the table, drop it, then create it again, and so on - in Oracle, we create table once and use it many times.

Creating an SQL FUNCTION that inserts data into two existing tables

I am trying to create a FUNCTION that will insert complete and relevant information into an existing table. I need the FUNCTION to check if certain entities exist in another table and, if not, inserts the data. Example code below:
CREATE FUNCTION insert_payment (customer_uuid uuid, customer_name varchar(63), payment_uuid uuid, total_amount integer
So let's say that I wanted to enter a payment into a table called Payments. I want the FUNCTION to check another existing table called Customers to see if customer_uuid and customer_name already exist within the table. If not, I would like the FUNCTION to insert the customer_uuid and customer_name information into Customers as well as enter the data from all four parameters into Payments.
This is my first question to ask on Stack Overflow so if greater clarification is needed please let me know. I am also a student and still learning how to communicate effectively when talking about coding so, again, if further clarification is needed I will try my best. Thank you!
You can make use of an insert on conflict do nothing to insert the customer and skip inserting if it already exists.
So with this table definition:
create table customer (id uuid primary key, name text);
create table payment (id uuid primary key, customer_id uuid not null references customer, amount integer);
This function would do what you want:
CREATE FUNCTION insert_payment (customer_uuid uuid, customer_name varchar(63),
payment_uuid uuid, total_amount integer)
returns void
as
$$
insert into customer (id, name)
values (customer_uuid, customer_name)
on conflict do nothing;
insert into payment (id, customer_id, amount)
values (payment_uuid, customer_uuid, total_amount);
$$
language sql;
This is just as efficient as first checking the existence (select exists (...)) before inserting, because the INSERT statement will do that check anyway.
Online demo
I can help you more after knowing your table fields name but here I'm sharing a code maybe it'll help you
//set variable
$customer_uuid= $_POST['customer_uuid'];
$customer_name= $_POST['customer_name'];
//WRITE QUERY to the check-in customers table
$duplicate_customer = $this->db->prepare( "SELECT customer_uuid,customer_name FROM customers WHERE customer_uuid = '$customer_uuid' AND customer_name='customer_name'" ); $duplicate_customer->execute();
//now check data if exist in the customer table
if($duplicate_customer->rowCount() > 0){
//write here insert query to insert data in customer table
}

PostgreSQL: Inserting tuples in multiple tables using a view and a trigger

I am trying to build an order system that is able to insert a compound order that consists of multiple items and amounts. My database layout is as follows: I have an order table, containing an autoincrement id, item_id, amount and order_group_id columns. I also have an order_group table containing an autoincrement id and a person_id column. The idea is that when a person orders, one new order_group entry is created, and its id is used as the fk in the orders that the person has done.
I presume that this would normally be done in the code of the application. However, I am using postgrest to provide an API for me, which suggests creating a custom view to insert compound entries via that route. This is described here.
This is what I have so far:
CREATE FUNCTION kzc.new_order()
RETURNS TRIGGER
LANGUAGE plpgsql
AS $$
DECLARE
group_id int;
BEGIN
INSERT INTO kzc.order_group (person) VALUES (new.person) RETURNING id AS group_id;
INSERT INTO kzc."order" (item, amount, order_group) VALUES (new.item_id, new.amount, group_id);
RETURN new;
END;
$$;
CREATE TRIGGER new_order
INSTEAD OF INSERT ON kzc.new_order
FOR EACH ROW
EXECUTE FUNCTION kzc.new_order()
However, this code makes a new ordergroup for every order that is in the compound insert. How can I make it so that my code only makes one new ordergroup entry and assigns its id to all orders?
Thanks in advance!
I suggest that you add an order_group_id column to the new_order view and create a sequence for it. Then create a DEFAULT value for the column:
ALTER VIEW kzc.new_order
ALTER order_group_id SET DEFAULT currval('order_group_id_seq');
Add a BEFORE INSERT trigger FOR EACH STATEMENT that just calls nextval for the sequence. The currval calls will all pick up the same generated value.
Then you have that number in your trigger and can use it as a primary key for order_group.
To avoid adding the row multiple times, use
INSERT INTO kzc.order_group (id, person)
VALUES (NEW.order_group_id, NEW.person)
ON CONFLICT (id) DO NOTHING;

Oracle PLSQL: insert ID that is in sequence of one table into another

I am trying to create a trigger that whenever you insert values inside a table called hotel, it takes some values from the field and inserts it inside another table called Restaurant.
My Hotel_ID is in sequence so I don't really give its value when I am using the insert statement to insert values inside the hotel table. Which creates problems for the restaurant table as it sets the value of Hotel_ID to null. I know in the insert statement I can just add the sequence name so that it works but the thing is, later on I have to convert my tables into APEX forms and with them, you can't insert the ID at all.
Anyway, here is my trigger that is basically supposed to take values from some fields of Hotel table and insert them into the Restaurant table:-
create or replace TRIGGER HOTELREST BEFORE INSERT ON HOTEL
FOR EACH ROW
BEGIN
IF INSERTING THEN
INSERT INTO RESTAURANT(CITY,ADDRESS,HOTEL_ID)
VALUES (:NEW.CITY,:NEW.ADDRESS,:NEW.HOTEL_ID);
END IF;
END;
Although the values of City and address get inserted inside the restaurant table, the value of hotel_id doesn't. Here is my insert statement for hotel:
INSERT INTO HOTEL(NAME,CITY,COUNTRY,ADDRESS,RATINGS)
VALUES ('Westin','Manchester','UK',5);
Now again, before you say that I didn't give an Id of the hotel, like I said, I am using sequence so when this row gets inserted inside the hotel table, a value for Hotel_Id is automatically generated. My trigger is also supposed to add Hotel_Id in my Restaurant table but it doesn't. It remains null. What do I do?

Best approach to insert delta records from view into a table

I have a requirement where I do a daily load from a view to a table. After the initial load, there may be scenarios where the original records get deleted from the view's source table. There are also scenarios where these records are updated.
When the stored procedure is run, the table that is loaded should pick up delta records. This means only new inserts. Also, it should mark deleted lines as D. In addition to this, any updates in source data must also updated in this table and marked as U.
Please refer to the attached image which shows in case 1 , 2 inserts on the initial load and then an update and then a delete.
Left side represents the view and right side represents the table I am trying to load.
Thanks!
Shyam
If you prefer to use triggers on HANA database tables you can use following samples on a column table, if you are working with row tables then you can prefer statement based approach
create trigger Salary_A_DEL after DELETE on Salary
REFERENCING OLD ROW myoldrow
FOR EACH ROW
begin
INSERT INTO SalaryLog (
Employee,
Salary,
Operation,
DateTime
) VALUES (
:myoldrow.Employee,
:myoldrow.Salary,
'D',
CURRENT_DATE
);
end;
create trigger Salary_A_UPD after UPDATE on Salary
REFERENCING NEW ROW mynewrow, OLD ROW myoldrow
FOR EACH ROW
begin
INSERT INTO SalaryLog (
Employee,
Salary,
Operation,
DateTime
) VALUES (
:mynewrow.Employee,
:mynewrow.Salary,
'U',
CURRENT_DATE
);
end;
create trigger Salary_A_INS after INSERT on Salary
REFERENCING NEW ROW mynewrow
FOR EACH ROW
begin
INSERT INTO SalaryLog (
Employee,
Salary,
Operation,
DateTime
) VALUES (
:mynewrow.Employee,
:mynewrow.Salary,
'I',
CURRENT_DATE
);
end;