Mysql update date relative to specific column update - mysql

I have a users_settings table on my db in which I store some data about the user. For some of that data I need to keep track of last modification, for example:
+--------------+-------------+
| Field | Type |
+--------------+-------------+
| feel | varchar(10) |
| feel_last | timestamp |
| other | varchar(10) |
| other_last | timestamp |
+--------------+-------------+
When I update the row with a new feel value I want to automatically write the current timestamp, is it possible to achieve this directly from mysql or I need to set the timestamp directly in the update query from my backend?
Update 1
As suggested I need to use a trigger, I written this trigger but there is some syntax error, can you help me to identify the error?
CREATE TRIGGER users_feel_last BEFORE UPDATE ON users
FOR EACH ROW BEGIN
IF NEW.feel <> OLD.feel THEN
SET NEW.feel_last := now();
END IF;
END;
UPDATE 2
For who use AWS RDS: the parameter "log_bin_trust_function_creators" isn't enabled by default, to create triggers it's necessary, here a small guide to set it: https://aws.amazon.com/it/premiumsupport/knowledge-center/rds-mysql-functions/
Now, thanks to James answer I created the trigger, it works well
DELIMITER $$
CREATE TRIGGER before_feel_last_update
BEFORE UPDATE ON users
FOR EACH ROW
BEGIN
if(old.feel<>new.feel)
then
set new.feel_last=current_timestamp;
elseif( old.feel = NULL )
then
set new.feel_last=current_timestamp;
end if;
END$$
DELIMITER ;

A trigger is not necessary in MySQL. This functionality is happily built in. You can define the table as:
create table users (
. . .,
update_datetime datetime default current_timestamp on update current_timestamp
);
You can read about this default in the documentation.

You can use the below trigger on BEFORE UPDATE
BEGIN
if(old.feel<>new.feel)
then
set new.feel_last=current_timestamp;
end if;
END

Related

How do I set a column to be unique, but only under a condition?

I have a table, which has an ID, a column "A" and other columns. If A is set to "this", there can't be another ID with it's column A set to "this". Else, there can be many entries of that ID with multiple values set for column "A".
Ex.:
ID | A | other columns
this is allowed:
1 | that | ...
1 | something | ...
1 | foo | ...
but this is not:
1 | this | ...
1 | that | ...
(the 3 dots mean it doesn't matter what data we have there)
I'm doing this on MySQL Workbench, so it would be much appreciated if your answer showed me how to do it there.
You need to create a trigger that will fire before every INSERT or UPDATE statement (for each row) and check whether or not your constraint is valid and the row can or cannot be added. AFAIK in MySQL you actually need to have two triggers (one for each action), but there is nothing stopping you from wrapping up your validation within a procedure and call it from both triggers.
I'll give you the starters for further tweaking.
1.Create insert trigger
DELIMITER //
DROP TRIGGER IF EXISTS insert_trg //
CREATE TRIGGER insert_trg
BEFORE UPDATE ON yourtable
FOR EACH ROW
BEGIN
CALL yourprocedure(<arguments here>);
END //
DELIMITER ;
2.Create update trigger analogously to the insert trigger
3.Create procedure with the validation code
SQL to validate your constraint will look something like:
SELECT *
FROM yourtable yt
WHERE yt.id = NEW.id -- replace with arguments passed to procedure
AND yt.A = NEW.A -- same as above
You most likely need to wrap it up with an EXISTS statement and if statement
Some source of knowledge:
Trigger Syntax and Examples
Create Procedure Syntax

UPDATE or INSERT on the same query without unique value

Hello Im trying to find a way the UPDATE or INSERT data with one query.
I have a table with like this:
+------+-------------+
| User | action_type |
+------+-------------+
| Jon | 1 |
| Kate | 2 |
| Jon | 4 |
+------+-------------+
I want to insert new value for Jon only if there is no values of Jon.
If I have values of Jon I want to update all the rows with Jon.
Ive read about INSERT ... ON DUPLICATE KEY UPDATE but I dont have unique values.
Thanks for helping.
you can count the entries for jon. If exists update else insert.
if you want to implement only using sql you can use an stored procedure
something like this:
CREATE PROCEDURE insertorupdate (IN name VARCHAR(20))
BEGIN
DECLARE numJon INT;
SELECT COUNT(*) INTO numJon FROM table WHERE User=name;
IF numJon > 0 THEN
// UPDATE ;
ELSE
// INSERT
END IF;
END
Then you can call you Store Procedure:
CALL insertorupdate('John');
If you can do it from your app you can call the same thing but separatelly. Do a select count, test if count if grater than 0 and then do the insert or the update on DB
Don't count like in user468891's answer. It might become a performance issue. Instead just check if an entry exists. As soon as an entry is found, the query returns true, count continues to find all records.
DELIMITER $$
CREATE PROCEDURE insertorupdate (IN name VARCHAR(20))
BEGIN
IF (EXISTS(SELECT 1 FROM table WHERE User = name)) THEN
// UPDATE...
ELSE
// INSERT...
END IF;
END $$
DELIMITER ;

How to track who changes a record in a table

I need to track who changes to a table in mySQL database. Would you please give me an idea how I can determine which of my application users makes changes to a record in a table?
I assume you have a table with 2 columns that their name is colmun1 and column2 You should add modified_by column and also add this trigger:
DELIMITER |
CREATE TRIGGER before_update_sometable
BEFORE UPDATE ON sometable FOR EACH ROW
BEGIN
IF (NEW.column1 <> OLD.column1 or NEW.column2 <> OLD.column2) THEN
NEW.modified_by = user();
END IF;
END;
|
DELIMITER ;

History field in MySQL database

Trying to create a field in a table that will get the old value of another field. My table is very similar to this:
------------ -------------------- ------------------------
| id int(10) | price decimal(5,2) | price_old decimal(5,2) |
----------------------------------------------------------
What I am trying to get is the value of price field to get copied in price_old field ON UPDATE of the current record.
Is it possible to achieve this only with mysql?
PS: The data contained in that cell is not critical. The usage is to store the previous price of a item and after to be able to show how it changed. I will only need the last value, not the full history of the table entry. (My MySQL server is 5.0, if this matters)
Yes, you can use a BEFORE UPDATE trigger (documentation).
I think this syntax is right but haven't used MySQL in a while so you may have to toy with it. If it's wrong and you get it working please let me know so I can edit it.
DELIMITER $$
CREATE TRIGGER `price_trigger`
BEFORE UPDATE ON `products`
FOR EACH ROW
BEGIN
IF NEW.price != OLD.price
SET price_old = OLD.price;
END IF;
END$$
DELIMITER ;
Use triggers on update rule. There set value of price to price_old
CREATE TRIGGER `update_price_old`
BEFORE UPDATE ON `table_name` FOR EACH ROW
BEGIN
UPDATE `table_name` SET price_old = OLD.price where OLD.id = id;
END

Is it possible to create a column with a UNIX_TIMESTAMP default in MySQL?

I'm trying to do this, but it seems like MySQL isn't allowing me. Is there a solution to this issue or am I expected to always include the function in my INSERT queries?
CREATE TABLE foo(
created INT NOT NULL DEFAULT UNIX_TIMESTAMP()
)
I'm aware of the TIMESTAMP type that accepts a CURRENT_TIMESTAMP default, but my client insisted on using epoch time in the database.
The way MySQL implements the TIMESTAMP data type, it is actually storing the epoch time in the database. So you could just use a TIMESTAMP column with a default of CURRENT_TIMESTAMP and apply the UNIX_TIMESTAMP() to it if you want to display it as an int:
CREATE TABLE foo(
created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
insert into foo values (current_Date()),(now());
select unix_timestamp(created) from foo;
+-------------------------+
| unix_timestamp(created) |
+-------------------------+
| 1300248000 |
| 1300306959 |
+-------------------------+
2 rows in set (0.00 sec)
However, if you really want the datatype of the column to be INT, you can use R. Bemrose's suggestion and set it via trigger:
CREATE TABLE foo(
created INT NULL
);
delimiter $$
create trigger tr_b_ins_foo before insert on foo for each row
begin
if (new.created is null)
then
set new.created = unix_timestamp();
end if;
end $$
delimiter ;
insert into foo values (unix_timestamp(current_Date())), (null);
select created from foo;
+------------+
| created |
+------------+
| 1300248000 |
| 1300306995 |
+------------+
2 rows in set (0.00 sec)
From the documentation:
With one exception, the default value
must be a constant; it cannot be a
function or an expression. This means,
for example, that you cannot set the
default for a date column to be the
value of a function such as NOW() or
CURRENT_DATE. The exception is that
you can specify CURRENT_TIMESTAMP as
the default for a TIMESTAMP column.
You can create triggers for this.
for insertion
query
CREATE TRIGGER {trigger_name} BEFORE INSERT ON {table_name} FOR EACH ROW SET new.{field_name} = UNIX_TIMESTAMP(NOW());
in this case
CREATE TRIGGER my_trigger_name_1 BEFORE INSERT ON foo FOR EACH ROW SET new.created = UNIX_TIMESTAMP(NOW());
for update
query
CREATE TRIGGER {trigger_name} BEFORE UPDATE ON {table_name} FOR EACH ROW SET new.{field_name} = UNIX_TIMESTAMP(NOW());
in this case
CREATE TRIGGER my_trigger_name_2 BEFORE UPDATE ON foo FOR EACH ROW SET new.created = UNIX_TIMESTAMP(NOW());
Note : I have no idea about the performance of MYSQL TRIGGER
Please go through these links
Identify some of the drawback of implementing sql server triggers
Using Triggers
Well, if MySQL won't let you do it directly, you can always do it using a BEFORE INSERT... FOR EACH ROW trigger.
As ktretyak's answer, with MySQL v8+, you can do that using a parentheses.
CREATE TABLE foo(
created INT NOT NULL DEFAULT (UNIX_TIMESTAMP())
)
After some inserts, you can see it works correctly.
(Tested on MySQL 8.0.26 + only text color patched)
Alternatively you can use an expression, too.
Check ktretyak's answer.
I write this answer instead of comment or edit answer(tried and rejected)
because of lack of REPUTATION and using a parentheses is good solution now.
Now, with MySQL v8+, you can to do this just in parentheses:
CREATE TABLE t1 (
-- literal defaults
i INT DEFAULT 0,
c VARCHAR(10) DEFAULT '',
-- expression defaults
f FLOAT DEFAULT (RAND() * RAND()),
b BINARY(16) DEFAULT (UUID_TO_BIN(UUID())),
d DATE DEFAULT (CURRENT_DATE + INTERVAL 1 YEAR),
p POINT DEFAULT (Point(0,0)),
j JSON DEFAULT (JSON_ARRAY())
);
See docs