MySQL Trigger inserts null instead of values - mysql

I'm having trouble understanding how triggers execute in MySQL and I'm banging my head on the wall, because I can't seem to find out why it doesn't work.
I have the following trigger
CREATE TRIGGER Insert_Products BEFORE INSERT ON `Products`
FOR EACH ROW BEGIN
DECLARE x_ProductID INT;
SET x_ProductID = NEW.`ProductID`;
SET NEW.`PriceExVAT` = (
SELECT
ROUND(p.`Price` * 100 / (100 + v.`VATPercentage`), 2) as priceexvat
FROM
`Products` p
LEFT JOIN
`VAT` v ON p.`VATID` = v.`VATID`
WHERE p.`ProductID` = x_ProductID); -- also tried inserting NEW.`ProductID` directly into this line
END $$
However it populates my rows with null instead of the correct values. HOWEVER, putting it in a select query results the correct values. IE:
SELECT
ROUND(p.`Price` * 100 / (100 + v.`VATPercentage`), 2) as x_value
FROM
`Products` p
LEFT JOIN
`VAT` v ON p.`VATID` = v.`VATID`
WHERE p.`ProductID` = 1;
I tried putting it in an AFTER INSERT trigger, but that resulted in a different error. What am I not seeing, how should I fix this?

You do not need to query the product-table for the values of the currently inserted row: apart from the autoincrement id, they are provided in NEW, and you can use them directly:
SET NEW.`PriceExVAT` = (
SELECT ROUND(NEW.`Price` * 100 / (100 + v.`VATPercentage`), 2) as priceexvat
FROM `VAT` v
WHERE NEW.`VATID` = v.`VATID`
)
You can do the same in an before update-trigger.

Related

Trigger syntax wrong

I'm trying to create a trigger to give "SumBeforeTaxes(from 'order')" a value.
The "SumBeforeTaxes" is itemQuantity(from 'order_item') x price ('from itemsinstock').
So if one has ordered 3 items at 10 USD each, the "SumBeforeTaxes" would be 3 x 10 (30 in total). Likewise, if one has ordered 3 items at 10 USD each AND 2 items at 15 each, the "SumBeforeTaxes" should be 3x10 + 2x15 (60 in total).
Here's what I've tried so far - but I keep getting syntax errors. I'm also not sure my triggers (despite the syntax errors) are correct.
1st attempt:
CREATE DEFINER = CURRENT_USER TRIGGER `orderdb`.`order_AFTER_INSERT`
AFTER INSERT ON `order_item` FOR EACH ROW
BEGIN
SET getItemPrice = (SELECT Price FROM itemsinstock);
INSERT orderdb.'order'
SET SumBeforeTax = itemQuantity * getItemPrice;
END
2nd attempt:
CREATE DEFINER = CURRENT_USER TRIGGER `orderdb`.`order_AFTER_INSERT`
AFTER INSERT ON `order_item` FOR EACH ROW
BEGIN
SET getItemPrice = (SELECT Price FROM itemsinstock);
INSERT INTO orderdb.'order' (SumBeforeTax) VALUES (new.itemQuantity * getItemPrice);
END
3rd attempt:
CREATE DEFINER = CURRENT_USER TRIGGER `orderdb`.`order_AFTER_INSERT`
AFTER INSERT ON `order_item` FOR EACH ROW
BEGIN
declare getItemPrice DOUBLE;
SET getItemPrice = (SELECT Price FROM itemsinstock WHERE ItemID = new.itemID);
INSERT INTO orderdb.'order'(SumBeforeTax) VALUES (itemQuantity * getItemPrice);
END
Can I get a little help on this one?
order is a reserve word in MySQL and thus you need to escape it like below using backtique for your INSERT statement.
INSERT INTO orderdb.`order`(SumBeforeTax) VALUES (itemQuantity * getItemPrice);
For any syntactical clarification refer Documentation On CREATE TRIGGER Syntax.
You might want to change your code like below in that case using an UPDATE statement rather:
BEGIN
declare getItemPrice DOUBLE;
SELECT Price INTO getItemPrice FROM itemsinstock WHERE ItemID = new.itemID;
UPDATE orderdb.`order` SET SumBeforeTax = new.itemQuantity * getItemPrice
WHERE OrderID = 101;

You can't specify target table 'my_table' for update in FROM clause

I am trying to create a SQL trigger which after updating the table will execute a piece of SQl to update it again
I have 5 fields that can be updated that each contain 0 or 1:
step1_complete, step2_complete, step3_complete, step4_complete and step5_complete
after any of these columns are updated I want to run a trigger that will update percent_complete within the same table with the following query:
SELECT sum( step1_complete +
step2_complete +
step3_complete +
step4_complete +
step5_complete ) * 20 AS Sum
FROM completed_part
GROUP BY id
this query will return either 20, 40, 60, 80 or 100 which is what i expect but when i try to run the update query to see if it works i get this error message
#1093 - You can't specify target table 'completed_part' for update in
FROM clause
I would also like to know if this is the correct syntax for creating a trigger
DELIMITER $$
CREATE
TRIGGER `completed_part_after_update` AFTER UPDATE
ON `completed_part`
FOR EACH ROW BEGIN
UPDATE completed_part
SET percent_complete = (
SELECT sum( step1_complete +
step2_complete +
step3_complete +
step4_complete +
step5_complete ) * 20 AS Sum
FROM completed_part
GROUP BY id
);
END$$
DELIMITER ;
try to use BEFOR UPDATE
BEGIN
SET NEW.percent_complete = ( NEW.step1_complete + NEW.step2_complete + NEW.step3_complete + NEW.step4_complete + NEW.step5_complete ) * 20;
END

How to pass value for wherein in procedure of mysql

DELIMITER //
DROP PROCEDURE IF EXISTS salary_ref//# MySQL returned an empty result set (i.e. zero rows).
# MySQL returned an empty result set (i.e. zero rows).
CREATE PROCEDURE salary_ref(con int(11),IN id varchar(120),maxval int(11),minval int(11) , taxo int(11))
BEGIN
DECLARE s VARCHAR(50);
IF con = 1 THEN
SELECT `i` . * , `taxo`.`id` , `t`.`item_id` AS id, `u`.`name` AS user_name, `t`.`value` AS val
FROM (
`taxonomy` AS taxo
)
JOIN `item_taxo` AS t ON `t`.`taxo_id` = `taxo`.`id`
INNER JOIN `items` AS i ON `i`.`id` = `t`.`item_id`
INNER JOIN `users` AS u ON `u`.`id` = `i`.`created_by`
WHERE `t`.`value`
BETWEEN minval
AND maxval
AND `t`.`taxo_id` = taxo
AND `i`.`parent_tag_id` in (id);
ELSE
SELECT `i` . * , `taxo`.`id` , `t`.`item_id` AS id, `u`.`name` AS user_name, `t`.`value` AS val
FROM (
`taxonomy` AS taxo
)
JOIN `item_taxo` AS t ON `t`.`taxo_id` = `taxo`.`id`
INNER JOIN `items` AS i ON `i`.`id` = `t`.`item_id`
INNER JOIN `users` AS u ON `u`.`id` = `i`.`created_by`
WHERE `t`.`value`
BETWEEN minval
AND maxval
AND `t`.`taxo_id` = taxo
AND `i`.`parent_tag_id` in (id);
END IF;
END;
//# MySQL returned an empty result set (i.e. zero rows).
DELIMITER ;
//calling of that
call salary_ref(2,"\'36\',\'50\',\'57\'",17000000,0,7)
this is not work for me.
Following changes would solve the issues.
Change 1:
I suggest to not use same parameter names for stored procedure to represent column names of tables.
Unless you handle them properly there would arise an identifier conflict but does not seem to be there.
Change procedure signature as follows:
CREATE PROCEDURE salary_ref(
_con int(11), IN csv_id_values varchar(120),
_maxval int(11), _minval int(11), _taxo int(11)
)
Change 2:
You are trying to pass comma separated values for id field to use in where clause.
But using escape character won't solve your problem and that is not correct way of using input values.
call salary_ref( 2, '36,50,57', 17000000, 0, 7 )
The CSV value '36,50,57' can be used as is for where clause.
See the suggested Change below.
Change 3:
You can use FIND_IN_SET on CSV values instead of IN in the WHERE clause.
WHERE `t`.`value` BETWEEN _minval AND _maxval
AND `t`.`taxo_id` = _taxo
AND FIND_iN_SET( `i`.`parent_tag_id`, csv_id_values );
And, I think your procedure body is incomplete. Your IF con and ELSE are using the same SELECT statement. It is redundant, unless you change it.

trigger for a compostite key and update another column

I have two tables: threads and threadreply.
threads has a composite primary key, (thread_id,reply_id).
When I insert a new row into threadreply, I need to:
update my column threads.reply_count by adding +1 to the previous value; and
insert data into threadreply by adding +1 to the MAX(reply_id) with same thread_id.
Using the following trigger, I was able to add+1 to my reply_id, but couldn't get to update my reply_count:
CREATE TRIGGER addone BEFORE INSERT ON threadreply
FOR EACH ROW BEGIN
SET NEW.reply_id = (
SELECT IFNULL(
(
SELECT MAX(reply_id) + 1
FROM threadreply
WHERE thread_id = NEW.thread_id
),
1
)
);
END
How can I solve this?
I assume that there is a typo in the question and the composite key (thread_id,reply_id) exists in the threadreply table, not in the threads table.
The trigger might look like:
CREATE TRIGGER addone BEFORE INSERT ON threadreply
FOR EACH ROW BEGIN
SET NEW.reply_id = (
SELECT IFNULL(
(
SELECT MAX(reply_id) + 1
FROM threadreply
WHERE thread_id = NEW.thread_id
), 1
)
);
UPDATE threads SET reply_count = reply_count + 1
WHERE thread_id = NEW.thread_id;
END
/
Take a look at this demo: --> http://www.sqlfiddle.com/#!2/1e7bb/2
The trigger and insert statements are on the left side in the schema window, below are shown results of the demo.

Finding min and max value of the table in a constant time

I have a table which contains relative large data,
so that it takes too long for the statements below:
SELECT MIN(column) FROM table WHERE ...
SELECT MAX(column) FROM table WHERE ...
I tried index the column, but the performance still does not suffice my need.
I also thought of caching min and max value in another table by using trigger or event.
But my MySQL version is 5.0.51a which requires SUPER privilege for trigger and does not support event.
It is IMPOSSIBLE for me to have SUPER privilege or to upgrade MySQL.
(If possible, then no need to ask!)
How to solve this problem just inside MySQL?
That is, without the help of OS.
If your column is indexed, you should find min(column) near instantly, because that is the first value MySQL will find.
Same goes for max(column) on an indexed column.
If you cannot add an index for some reason the following triggers will cache the MIN and MAX value in a separate table.
Note that TRUE = 1 and FALSE = 0.
DELIMITER $$
CREATE TRIGGER ai_table1_each AFTER INSERT ON table1 FOR EACH ROW
BEGIN
UPDATE db_info i
SET i.minimum = LEAST(i.minimum, NEW.col)
,i.maximum = GREATEST(i.maximum, NEW.col)
,i.min_count = (i.min_count * (new.col < i.minumum))
+ (i.minimum = new.col) + (i.minimum < new.col)
,i.max_count = (i.max_count * (new.col > i.maximum))
+ (i.maximum = new.col) + (new.col > i.maximum)
WHERE i.tablename = 'table1';
END $$
CREATE TRIGGER ad_table1_each AFTER DELETE ON table1 FOR EACH ROW
BEGIN
DECLARE new_min_count INTEGER;
DECLARE new_max_count INTEGER;
UPDATE db_info i
SET i.min_count = i.min_count - (i.minimum = old.col)
,i.max_count = i.max_count - (i.maximum = old.col)
WHERE i.tablename = 'table1';
SELECT i.min_count INTO new_min_count, i.max_count INTO new_max_count
FROM db_info i
WHERE i.tablename = 'table1';
IF new_max_count = 0 THEN
UPDATE db_info i
CROSS JOIN (SELECT MAX(col) as new_max FROM table1) m
SET i.max_count = 1
,i.maximum = m.new_max;
END IF;
IF new_min_count = 0 THEN
UPDATE db_info i
CROSS JOIN (SELECT MIN(col) as new_min FROM table1) m
SET i.min_count = 1
,i.minimum = m.new_min;
END IF;
END $$
DELIMITER ;
The after update trigger will be some mix of the insert and delete triggers.