SQL Server trigger : update query new vs old data - sql

I have two tables. One has employee info in it. The other I want to add records to based on this employee table.
What I want to happen is whenever there are salary adjustments made to the employee table (by an UDPATE query), the extra table will have a row added to it containing a new event ID and the amount by which salaries have been adjusted (so if 5 people's salaries are increased by £1000, then the row will have the adjustment at £5000).
I've created the trigger and it adds the row with each update. However what it doesn't do is only bring in the additional salary. I can't think how to do that. I have this code so far;
Create trigger Salaryupdate
On TBL_Employees
For update
As
Insert TBL_audit (notes,Delta,AdjDate)
Select 'Salary update', sum(salary), getdate()
From TBL_Employees
I know the sum bit is wrong as I only want the change in salary value, not the total sum.
How can I find the difference between new and old values for the changed rows (or other method)?
I'm using SQL Server 2008.

You should be using the deleted and inserted tables in a trigger. So, I think:
Create trigger Salaryupdate
On TBL_Employees
For update
As
Insert TBL_audit(notes, Delta, AdjDate)
Select 'Salary update',
coalesce(newsalary, 0) - coalesce(oldsalary, 0),
getdate()
From (select sum(salary) as newsalary from inserted) i cross join
(select sum(salary) as oldsalary from deleted) d;
Also, in SQL Server you can set AdjDate to have a default value of getdate() -- that way, the database takes care of setting the value when you insert another row.

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.

How do I get results of SQL data change statement with triggers updates applied?

I have a requirement where I need to get the immediate result of an update statement. I saw that I can do that by using the SQL data-change-statement modifiers. However, I'm not being able to get the final result after applying all associated triggers. For example, let's say I have the following query:
SELECT empno, salary FROM FINAL TABLE
(UPDATE employee SET salary = salary * 1.10 WHERE job = 'CLERK')
And let's suppose I have a trigger that does the following:
CREATE TRIGGER EXTRA_PAY_RISE AFTER UPDATE ON employee
REFERENCING OLD AS oldrow
NEW AS newrow
FOR EACH ROW MODE DB2SQL
WHEN (newrow.dept = 'sales')
UPDATE employee SET salary = salary * 1.01 WHERE name = newrow.name;
How can I get the result from the first select statement containing the updates applied by all of the associated triggers (if that's possible)?
Use BEFORE UPDATE trigger and either NEW TABLE (in any case) or FINAL TABLE (if you don't have AFTER UPDATE triggers).
If you can't use BEFORE trigger to implement your update logic, then you can't use a data-change-statement to achieve your goal.

SQL Trigger to allow only customers who are old enough to buy a book?

I have 3 tables, person, audiobook and audiobook_purchases. My database is running MariaDB.
person has fields: id, date_of_birth;
audiobook has fields: ISBN, age_rating, title;
audiobook_purchases has fields: ISBN, customer_id, date_of_purchase;
I'm trying to write a trigger to make sure that when a customer tried to purchases an audiobook, they are old enough to do so according to the age_rating in audiobook.
For example, If Harry Potter and the Philosipher's Stone had age rating 16 and customer Jennifer (ID 1) with date of birth 2010-01-01 tried to purchase this book, this would not be allowed, but Dominick(ID 2) with date_of_birth 1978-01-01 would be allowed.
Please could someone show me a way to run this trigger?
I don't know MariaDB in particular, so my answer may need some adjustments.
You want to create an insert trigger on audiobook_purchase so that a new order will be inserted only if the person who wants to place the order is old enough according to audiobook.age_rating.
First you need to figure out a way of extracting the year from person.date_of_birth. Something like the YEAR() scalar function will probably be available. MariaDB may also provide a NOW() function, which gives the current date. So the person age right now will be: YEAR(NOW()) - YEAR(person.date_of_birth).
Then you have to write the insert trigger. The tricky part is to query the person table to get the person date_of_birth from his id, then to compare it to audiobook.age_rating.
Let's set out an example. First we declare the tables schemas:
CREATE TABLE person(id, name, date_of_birth);
CREATE TABLE audiobook(isbn, age_rating, title);
CREATE TABLE audiobook_purchases(isbn, customer_id, date_of_purchase);
Then we put in some data:
INSERT INTO person VALUES (10, "jennifer", '2010-01-01');
INSERT INTO person VALUES (20, "dominick", '1978-01-01');
INSERT INTO audiobook VALUES (1234, 16, "harry potter");
Then we create the trigger:
CREATE TRIGGER check_purchases
AFTER INSERT ON audiobook_purchases
FOR EACH ROW
WHEN (
SELECT strftime('%Y', 'now') - strftime('%Y', date_of_birth) AS age
FROM person
WHERE new.customer_id=person.id) < (
SELECT audiobook.age_rating
FROM audiobook
WHERE audiobook.isbn=new.isbn)
BEGIN
DELETE FROM audiobook_purchases
WHERE isbn=new.isbn AND
customer_id=new.customer_id AND
date_of_purchase=new.date_of_purchase;
END;
I'll broke down the trigger into smaller steps:
AFTER INSERT ON audiobook_purchases creates a trigger on table audiobook_purchases which will be triggered after the insertion of a new record.
FOR EACH ROW applies the trigger to each new record inserted.
The WHEN clause limits triggering only to those records who satisfy its condition. On the left side of the < sign of the condition there is a query which selects the age of the customer. On the right side there is a query which selects the age rating of the book. Notice the reference to a new table. This table stores the record which triggers the event (see the two examples below). strftime is a scalar function which formats datetime stamps in SQLite. You can read:
strftime('%Y', 'now') as YEAR(NOW()) and
strftime('%Y', date_of_birth) as YEAR(date_of_birth).
Finally between BEGIN and END there are instructions that will be executed on triggering. In this case there is a single instruction which removes the record just inserted. MariaDb may provide a ROLLBACK statement, which can be more efficient than the DELETE statement.
So, for example:
INSERT INTO audiobook_purchases VALUES (1234, 10, '2018-11-25');
will activate the trigger, because the customer with id=10 ('jennifer') is 8 years old and the book with isbn=1234 requires the customer to be at least 16 years old, while:
INSERT INTO audiobook_purchases VALUES (1234, 20, '2018-11-25');
will not activate the trigger, because this customer is 40 years old.
You must be aware that this solution silently ignore the invalid order. I don't know if this is your desired behaviour.
I tested this trigger on SQLite 3.11.0, so it may not be compatible with your SQL interpreter.

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;

Creating SQL trigger that only affects specific row

I have two tables created inventory and customer_sales. Individuals working in the warehouse can't have access to the customer_sales table.
So I added an additional field to inventory table called num_sales.
I want to create a trigger functions that whenever a new customer sale is added. It will correspond to the specific inventory_item that was sold's row and not the entire table.
This is what I have so far.
ALTER TABLE inventory
ADD num_sales INT
UPDATE movies SET num_sales = 0;
ALTER TABLE movies ALTER COLUMN num_rentals INT NOT NULL;
GO
CREATE TRIGGER tr_movies_num_rentals_add
ON customer_sales FOR INSERT
AS
BEGIN
UPDATE inventory
SET num_sales = num_sales + (SELECT sale_status_code FROM INSERTED)
WHERE 1 = (SELECT sale_status_code FROM INSERTED);
END;
Note:
sale_status_code values: 1=sold, 2=reserved, 3=lost.
UPDATE: I am using Microsoft SQL server management studio. I am newbie and this is a question I have for school.
First assume inserted willhave multiple rows. SQL server triggers do not operate row by row. YOu need to join to inserted not use a subquery. Next if you want this to happen on insert, update and delete, then you need to use more than an INSert trigger. And the delete trigger should use a different formula than inserted and updated becasue you will be subtracting the value from the deleted table from the inventory total.