MySQL Trigger Value from Previous Row with Same Column Name - mysql

Inventory Table:
Inventory History Table:
The query:
INSERT INTO inventory_history (SKU, Quantity, timestamp)
SELECT SKU, Quantity, modifiedtime FROM inventory WHERE modifiedtime BETWEEN '2016-12-25 00:00:00' AND '2016-12-26 00:00:00';
The Trigger:
CREATE TRIGGER `sold_diff` BEFORE INSERT ON `inventory_history`
FOR EACH ROW begin
declare prev_quantity int(11) default 0;
declare prev_sku varchar(255) default null;
select sku
into prev_sku
from inventory_history
where prev_sku = NEW.sku
order by id desc
limit 1;
select quantity
into prev_quantity
from inventory_history
order by id desc
limit 1;
set NEW.sold = prev_quantity
;
end
The Result:
Now, how it's set-up is it's taking prev_quantity from the previous row, and putting it into the sold column.
I can not figure out a way to bind SKU in with prev_quantity, so that it will give me the previous Quantity value from the corresponding SKU.
Desired Result:
I've messed with a variety of different WHERE clauses on the two declared, but nothing is working right.. so I'm thinking this is not the right path to take.
How can this be achieved?

I think you are taking the wrong approach.
You seem to want an insert on the inventory table. When a new value is inserted or updated (or deleted), you then insert a row in the inventory_history table with the old and new values.
You then don't need an explicit insert on inventory_history.

Related

SQL to find if refund would be processed or not

I have 2 tables, they can be created with the following query:
CREATE TABLE transactions(Id integer,ptime date, rtime date, sid text, itemid text, gtv integer);
/* Create few records in this table */
INSERT INTO transactions VALUES(3,'2019-09-19',null,'a','a1',58);
INSERT INTO transactions VALUES(12,'2019-12-10','2019-12-15','b','b2',475);
INSERT INTO transactions VALUES(3,'2020-09-01','2020-09-02','f','f9',33);
INSERT INTO transactions VALUES(2,'2020-04-30',null,'d','d3',250);
INSERT INTO transactions VALUES(1,'2020-10-22',null,'f','f2',91);
INSERT INTO transactions VALUES(8,'2020-04-16',null,'e','e7',24);
INSERT INTO transactions VALUES(5,'2019-09-23',null,'g','g6',61);
CREATE TABLE Items(sid text , itemid text, category text, name text);
/* Create a few records in this table */
INSERT INTO Items VALUES('a','a1','pants','denimpants');
INSERT INTO Items VALUES('a','a2','tops','blouse');
INSERT INTO Items VALUES('f','f1','table','coffee table');
INSERT INTO Items VALUES('f','f5','chair','loungechair');
INSERT INTO Items VALUES('f','f6','chair','armchair');
INSERT INTO Items VALUES('d','d2','jewelry','bracelet');
INSERT INTO Items VALUES('b','b4','earphone','airpods');
Select * from NAMES;
COMMIT;
Create a flag in the transaction items table indicating whether the refund can be processed or not. The condition for a refund to be processed is that it has to happen within 72 of Purchase time.
Expected Output: Only 1 of the three refunds would be processed in this case
Create a rank by buyer_id column in the transaction items table and filter for only the second purchase per
buyer. (Ignore refunds here)
Expected Output: Only the second purchase of buyer_id 3 should the output
How will you find the second transaction time per buyer (don’t use min/max; assume there were more
transactions per buyer in the table)
Expected Output: Only the second purchase of buyer_id along with a timestamp
I have been trying to wrap my head around this, but cant seem to understand where to start.
Here is a test script treating the questions. NB I am running MariaDB. There may be adjustments needed for mySQL
We set up the database, written to be reusable.
USE test;
DROP TABLE IF EXISTS transactions;
DROP TABLE IF EXISTS items;
CREATE TABLE IF NOT EXISTS transactions(Id integer,ptime date, rtime date, sid text, itemid text, gtv integer);
/* Create few records in this table */
INSERT INTO transactions VALUES(3,'2021-09-19',null,'a','a1',58);
INSERT INTO transactions VALUES(12,'2021-12-10','2022-03-15','b','b2',475);
INSERT INTO transactions VALUES(3,'2021-09-01','2021-09-02','f','f9',33);
INSERT INTO transactions VALUES(2,'2021-12-30',null,'d','d3',250);
INSERT INTO transactions VALUES(1,'2021-10-22',null,'f','f2',91);
INSERT INTO transactions VALUES(8,'2021-04-16',null,'e','e7',24);
INSERT INTO transactions VALUES(5,'2022-01-23',null,'g','g6',61);
CREATE TABLE IF NOT EXISTS items(sid text , itemid varchar(25) PRIMARY KEY, category text, name text);
/* Create a few records in this table */
INSERT INTO items VALUES('a','a1','pants','denimpants');
INSERT INTO items VALUES('a','a2','tops','blouse');
INSERT INTO items VALUES('f','f1','table','coffee table');
INSERT INTO items VALUES('f','f5','chair','loungechair');
INSERT INTO items VALUES('f','f6','chair','armchair');
INSERT INTO items VALUES('d','d2','jewelry','bracelet');
INSERT INTO items VALUES('b','b4','earphone','airpods');
COMMIT;
Select * from transactions;
Select * from items;
Here we add a virtual column which gives us the refund status using the sales date and the request date.
request date before sales date = error
no request date = Not requested
request date 0 to 72 days after sales date = accepted
request date more then 72 days after sales date = too late
ALTER TABLE transactions ADD COLUMN refund VARCHAR(20) AS (CASE WHEN rtime is NULL THEN 'Not requested' WHEN rtime < ptime THEN "Error" WHEN DATEDIFF(rtime,ptime) > 72 THEN "too late" ELSE "accepted" END);
Select ptime,rtime,datediff(ptime,rtime) dif,refund from transactions;
To get the 2nd purchase using rank we create a query assigning rank as follows:
SELECT ID,
ptime,
RANK() OVER(PARTITION BY ID ORDER BY ptime ASC ) Rank
FROM transactions
ORDER BY ID,
Rank;
Which we can then use as a CTE and use a where to only return the second record for each I'd.
WITH rankings AS (
SELECT ID,
ptime,
RANK() OVER(PARTITION BY ID ORDER BY ptime ASC ) Rank
FROM transactions
ORDER BY ID,
Rank )
SELECT ID,ptime
FROM rankings
WHERE Rank = 2;
This is all very well but the request was for a virtual column and not a query.
When we try to use aggregation functions in a virtual column we get errors because the definition of a virtual column should only reference fields in the same row, of columns already declared in the table definition. (These lines are commented out to avoid blocking the script.
Gives an error
#ALTER TABLE transactions ADD COLUMN rank_id INT AS ( RANK() OVER ( PARTITION BY ID ORDER BY ptime ASC ));
#ERROR 1901 (HY000) at line 38: Function or expression 'rank()' cannot be used in the GENERATED ALWAYS AS clause of `rank_id`
#ALTER TABLE transactions ADD COLUMN rank_id INT AS ( RANKX(transactions.id,sum(idl)));
#ERROR 1901 (HY000) at line 41: Function or expression 'sum()' cannot be used in the GENERATED ALWAYS AS clause of `rank_id`
#ALTER TABLE transactions ADD COLUMN rank_id INT AS ( RANKX(transactions.id,id));
#ERROR 1901 (HY000) at line 55: Function or expression '`RANKX`()' cannot be used in the GENERATED ALWAYS AS clause of `rank_id`
It seems that some aggregation functions can be used in virtual columns some SQL engines. I've tried a few but I have yet to find one which works here.
I am wondering whether this is a "trick" question to encourage you to research the principales of virtual columns?

MYSQL insert only if values different

I'm trying to insert a new row with the same ID into a simple table but only want to insert if the values are different.
This table is to track price history of an item. The table has the following columns:
id, timestamp, productID, price
I only want to insert a new record if either the product doesn't exist or the product does exist but the price has changed.
Unfortunately I'm having a brain block due to my limited knowledge and would appreciate help in where to turn so I don't have any trials at the code to do this.
Thanks!
you can try something like this:
SET #PRODUCT = 1; # product id
SET #PRICE = 1; # new product price
insert into `t`(`product`, `timestamp`, `price`)
select v.product, now(), v.price
from
(select #PRODUCT as `product`, #PRICE as `price`) as v
left outer join
(select `product`, `price` from `t` where `product`=#PRODUCT order by `id` desc limit 1) as p
on (v.product=p.product)
where
(p.price is null) or
(p.price <> v.price);
so, this statement either insert new row (for new product or new price) or does nothing
u need composite primary key
ALTER TABLE products ADD PRIMARY KEY(product,price);
after this query if you insert if the product and price is same in your table returns error with duplicate entry
or it will insert the query even one field value changes

mysql trigger on update to populate another table

I am new to this TRIGGER thing and it is beating me up pretty good.
The intention is to populate a price history table every time a price change occurs on table prices.
So far my attempts are taking all prices changes each time one price is updated, or not working at all.
I basically did not get this logic, so that I can limit it to the one price being updated, not all of the updates.
This below is a bit of a pseudo-code.
DELIMITER //
CREATE TRIGGER price_update
AFTER UPDATE
ON prices FOR EACH ROW
BEGIN
INSERT INTO prices_history (version_id,price,data_change)
SELECT version_id, price, time_update FROM prices WHERE????? ;
END //
DELIMITER ;
table prices (summarized)
version_id smallint(4) primary
price decimal (10,0)
time_update timestamp
table prices_history
price_id int(5) primary
version_id smallint(4)
price decimal (10,0)
data_change datetime
Don't do SELECT inside the trigger, as that will use all the rows from table. You have available the columns from the changed row directly. Also you can use OLD. and NEW. for distinguishing between the values before and after UPDATE.
DELIMITER $$
CREATE TRIGGER price_update
BEFORE UPDATE ON prices
FOR EACH ROW
BEGIN
INSERT INTO prices_history SET
price = NEW.price,
version_id = NEW.version_id, // or OLD.version_id
data_change = NEW.time_update;
END$$
DELIMITER ;
prices table should have price_id - not version_id
Then
select #max_version = max(version_no)+1 from price_history where price_id = #changed_price_id
INSERT INTO prices_history (price_id, version_id,price,data_change)
SELECT price_id_id, #max_version, price, time_update FROM prices WHERE price_id = #changed_price_id
;

SQL Server 2008 trigger doesn't work, why?

I have a table called tblReservations with follwing columns:
reserv_ID (int), aptID (int), client_ID (int),
start_date (datetime), end_date (datetime),
details (nvarchar(max)), confirmation (bit)
What trigger should is to compare two dates start_date for new reservation and end_date for existing reservation in tblReservation for specific aptID.
If start_date < end_date trigger must prevent insertion of new reservation for that aptID.
I wrote this trigger:
CREATE TRIGGER NewReservation
on tblReservations
after insert
as
begin
declare #aptID int
declare #start_date datetime
declare #end_date datetime
select #aptID=aptID, #start_date=start_date from inserted
select #end_date=end_date from tblReservations
where aptID=#aptID
if #end_date>#start_date
BEGIN
ROLLBACK TRANSACTION
END
end
Why does this trigger not work?
please help
Aside from the multiorw issue that others have brought up, you are likely not considering that there are many rows in the tblReservations for a particular apt id.
Plus you do not want to insert the record and then get rid of it, you want the record to not go in. therefore an instead of trigger is a better bet.
CREATE TRIGGER NewReservation
ON tblReservations
INSTEAD OF INSERT
AS
BEGIN
INSERT tblReservations (<put field list here>)
SELECT <put field list here>
FROM inserted i
JOIN (SELECT aptid, MAX(reservationid)AS reservationid FROM tblReservations GROUP BY aptid) maxid
ON i.aptid = r.aptid
JOIN tblReservations r
ON r.reservationid = maxid.reservationid
WHERE r.enddate<i.startdate
END
The first problem I see is you are assuming inserted is a single row.
Try:
Select top 1 #aptID=bb.aptID, #start_date=start_date
from inserted as aa
join
(
select Max(start_date) as Start_Date from inserted
) as bb
on aa.Start_Date =bb.Start_Date
But you should think about changing your logic so that the record never gets inserted in the first place if the date is wrong.

Trigger returns results from all rows instead of one row

I'm trying to create a trigger on the employeepayhistory table. Whenever I update the RATE column in the table its effect on the monthly salary should be displayed, but instead it gives me the monthly salary of the whole record if I update one record. Please help.
CREATE TRIGGER MONTHTRG
ON HumanResources.EmployeePayHistory
FOR UPDATE
AS
BEGIN
DECLARE #Rate MONEY
SELECT #Rate=Rate FROM INSERTED
SELECT 'MONTHSAL'=Rate*PayFrequency*30 FROM HumanResources.EmployeePayHistory
END
You also need to give the where condition, typically you'll
DECLARE #Id bigint
SET #Id = (SELECT Id from Inserted)
SELECT 'MONTHSAL' = Rate * PayFrequency*30 From HumanResources.EmployeePayHistory where Humanresources.EmployeeID=#Id
This will work if the Primary key is called 'Id' and ofcourse, is unique. You can also do this with a Unique key(which should be Non-NULL)