Given the following table:
id | value
--------------
1 6
2 70
Is there a way to add a column that is automatically calculated based on another column in the same table? Like a VIEW, but part of the same table. As an example, calculated would be half of value. Calculated should be automatically updated when value changes, just like a VIEW would be.
The result would be:
id | value | calculated
-----------------------
1 6 3
2 70 35
Generated Column is one of the good approach for MySql version which is 5.7.6 and above.
There are two kinds of Generated Columns:
Virtual (default) - column will be calculated on the fly when a
record is read from a table
Stored - column will be calculated when a
new record is written/updated in the table
Both types can have NOT NULL restrictions, but only a stored Generated Column can be a part of an index.
For current case, we are going to use stored generated column. To implement I have considered that both of the values required for calculation are present in table
CREATE TABLE order_details (price DOUBLE, quantity INT, amount DOUBLE AS (price * quantity));
INSERT INTO order_details (price, quantity) VALUES(100,1),(300,4),(60,8);
amount will automatically pop up in table and you can access it directly, also please note that whenever you will update any of the columns, amount will also get updated.
If it is a selection, you can do it as:
SELECT id, value, (value/2) AS calculated FROM mytable
Else, you can also first alter the table to add the missing column and then do an UPDATE query to compute the values for the new column as:
UPDATE mytable SET calculated = value/2;
If it must be automatic, and your MySQL version allows it, you can try with triggers
MySQL 5.7 supports computed columns. They call it "Generated Columns" and the syntax is a little weird, but it supports the same options I see in other databases.
https://dev.mysql.com/doc/refman/5.7/en/create-table.html#create-table-generated-columns
#krtek's answer is in the right direction, but has a couple of issues.
The bad news is that using UPDATE in a trigger on the same table won't work. The good news is that it's not necessary; there is a NEW object that you can operate on before the table is even touched.
The trigger becomes:
CREATE TRIGGER halfcolumn_update BEFORE UPDATE ON my_table
FOR EACH ROW BEGIN
SET NEW.calculated = NEW.value/2;
END;
Note also that the BEGIN...END; syntax has to be parsed with a different delimiter in effect. The whole shebang becomes:
DELIMITER |
CREATE TRIGGER halfcolumn_insert BEFORE INSERT ON my_table
FOR EACH ROW BEGIN
SET NEW.calculated = NEW.value/2;
END;
|
CREATE TRIGGER halfcolumn_update BEFORE UPDATE ON my_table
FOR EACH ROW BEGIN
SET NEW.calculated = NEW.value/2;
END;
|
DELIMITER ;
You can use generated columns from MYSQL 5.7.
Example Usage:
ALTER TABLE tbl_test
ADD COLUMN calc_val INT
GENERATED ALWAYS AS (((`column1` - 1) * 16) + `column2`) STORED;
VIRTUAL / STORED
Virtual: calculated on the fly when a record is read from a table (default)
Stored: calculated when a new record is inserted/updated within the
table
If you want to add a column to your table which is automatically updated to half of some other column, you can do that with a trigger.
But I think the already proposed answer are a better way to do this.
Dry coded trigger :
CREATE TRIGGER halfcolumn_insert AFTER INSERT ON table
FOR EACH ROW BEGIN
UPDATE table SET calculated = value / 2 WHERE id = NEW.id;
END;
CREATE TRIGGER halfcolumn_update AFTER UPDATE ON table
FOR EACH ROW BEGIN
UPDATE table SET calculated = value / 2 WHERE id = NEW.id;
END;
I don't think you can make only one trigger, since the event we must respond to are different.
I hope this still helps someone as many people might get to this article. If you need a computed column, why not just expose your desired columns in a view ? Don't just save data or overload the performance with triggers... simply expose the data you need already formatted/calculated in a view.
Hope this helps...
Related
I'm attempting to create a trigger that increases the value of a column INCOME in the Salary database by 500 each time the value of WorkYear in the Employee table is increased by one year. For example, if the workYear is 4 and the salary is 1000, the salary should be 1500 if the workYear is increased by one year, 2000 if the workYear is increased by two years, and so on.
I tried to create such trigger and here is my code :
DELIMITER $$
create trigger increment AFTER UPDATE on employee
for each row
BEGIN
IF OLD.workYear <> new.workYear THEN
update salary
set income = (income + (new.workYear-old.workYear)*500);
END IF;
END$$
The idea behind this code is that after we update the workYear, the trigger should increase the salary by the difference of years * 500, (new.workYear-old.workYear)*500, but it increases all the rows by the same number, (5500 if we add one year, 27500 if we add two years, etc.) which not what we are looking for .
I am new to MySQL and would appreciate it if someone could assist me with this.
Thanks in advance
FaissalHamdi
In MySQL an AFTER trigger can affect the entire table, so you must declare the update scope in the form of criteria or a join.
Create Trigger in MySQL
To distinguish between the value of the columns BEFORE and AFTER the DML has fired, you use the NEW and OLD modifiers.
The concept is similar but each RDBMS has a slightly different syntax for this, be careful to search for help specifically on your RDBMS.
In the original query these special table references were used to evaluate the change condition however the scope of the update was not defined.
Assuming that there is a primary key field called Id on this salary table.
Also note that if you can, the query should be expressed in the form of a set-based operation, instead of static procedural script, this will be more conformant to other database engines.
So lets try this:
DELIMITER $$
create trigger increment AFTER UPDATE on employee
for each row
BEGIN
UPDATE salary s
SET income = (income + (new.workYear-old.workYear)*500)
WHERE s.Id = OLD.Id
END$$
I want to update a column in MySQL only if the row is updated. (meaning all the other values trigger mysql to update the row.)
It's a concatenated text-field, so automatic calculation using ON UPDATE in the table definition won't work.
A trigger won't work either since the value is not fixed.
If there is ON UPDATE in table definition or triggers, there must be a way to determine that in some expression, right?
(Of course, I could create/update a trigger every time after the update or do a second update based on automatic update timestamp (which I then need to select first...) but that's both not very effective nor elegant.)
What I'd love to do would be something like this:
UPDATE tbl
SET x = 1, y = 2
ON UPDATE ( z = CONCAT_WS(', ',z,'blah') );
I have a mysql Innodb table 'classrooms_subjects' as
id|classroom_id|subject_id
classroom_id & subject_id are composite keys. Whenever i insert a row with classroom_id & subject_id, my id field is inserted as 0.
Now i want to create a trigger which will enter id field as last_inserted_id()+1.
Also I need to take care of multiple records inserted at a time. My trigger is like below:
CREATE TRIGGER `increment_id` AFTER INSERT ON `classrooms_subjects`
FOR EACH ROW BEGIN
UPDATE classrooms_subjects
SET classrooms_subjects.id = LAST_INSERT_ID() + 1 WHERE id=0;
END
when i am inserting a record I am getting the error as:
"Cant update table in trigger because it is already used by statement which invoked this trigger
For general info: using an update statement inside the trigger isn't right.
Better to use a before insert trigger and simply assign the value of your column using NEW.id
http://dev.mysql.com/doc/refman/5.7/en/trigger-syntax.html
A column named with OLD is read only. You can refer to it (if you have
the SELECT privilege), but not modify it. You can refer to a column
named with NEW if you have the SELECT privilege for it. In a BEFORE
trigger, you can also change its value with SET NEW.col_name = value
if you have the UPDATE privilege for it. This means you can use a
trigger to modify the values to be inserted into a new row or used to
update a row. (Such a SET statement has no effect in an AFTER
trigger because the row change will have already occurred.)
You should probably structure your table to make the auto_increment work properly. Better a solution that works when multiple sessions are inserting to the DB at once.
I am using mySQL
Is there any way to set limit of table record/row? I have table X and want to set limit of total records/rows on table, for example 2rows. So no one can insert third record in table. This table should not allow to insert third record.
I do not want to use Triggers.
You can do it this the user grants, so the user cant write into this table and you can create a separate User for administration this table.
seee Manual : https://mariadb.com/kb/en/mariadb/grant/
If you dont want to use triggers, you 'll have to check the number of rows inside your application (if any).
Create an AFTER INSERT trigger on the table. - it's the only way to do it.
create trigger TableLimit
on TableName
after insert
as
declare #countTableRows int
select #countTableRows = Count(*)
from TableName
if #countTableRows > 2
begin
rollback
end
go
How would one implement this in MySQL:
CREATE TABLE employee (
employeemonthly DECIMAL(10,2),
employeeyearly DECIMAL(10,2) DEFAULT employeemonthly*12
);
use a insert trigger for that. Something like this
DELIMITER |
CREATE TRIGGER default_yearly BEFORE INSERT ON employee
FOR EACH ROW BEGIN
SET NEW.employeeyearly = NEW.employeemonthly * 12;
END;
|
DELIMITER ;
I would use a view:
CREATE VIEW vemployees AS
SELECT e.employeemonthly,
e.employeemonthly * 12 AS employeeyearly
FROM EMPLOYEE e
...because there's little need to dedicate storage space for an easily calculated value. Otherwise, use a function or simply write the expression into whatever query/stored procedure you need.
What really depends is:
How often you need to access this data
How complex the operation is to get the result you need
I recommend starting with not storing the value. If performance gets to be a problem, then dedicate a column for the values storage -- not before. At that, a trigger is a bit overkill to me, when you can use (psuedocode):
INSERT INTO employee
(...employeemonthly, employeeyearly, ...)
VALUES
(...#employeemonthly, #employeemonthly * 12, ...
Use a trigger for the insert event, access the new record data using NEW and set the appropiate values.