I have the following stored procedure that takes a key and a timestamp and updates the timestamp using the key if the key is already present, otherwise it inserts the key along with the accompanying timestamp value:
CREATE PROCEDURE `sp_AddUpdateTimestamp`(IN timestampKey VARCHAR(50), IN timestampValue TIMESTAMP)
BEGIN
IF(timestampKey IS NOT NULL) THEN
IF(timestampValue IS NOT NULL) THEN
IF EXISTS (SELECT Name FROM StoredTimestamps WHERE Name = timestampKey) THEN
UPDATE StoredTimestamps SET Timestamp = timestampValue WHERE Name = timestampKey;
ELSE
INSERT INTO StoredTimestamps (Name, Timestamp) VALUES (timestampKey, timestampValue);
END IF;
ELSE
IF EXISTS (SELECT Name FROM StoredTimestamps WHERE Name = timestampKey) THEN
UPDATE StoredTimestamps SET Timestamp = NOW() WHERE Name = timestampKey;
ELSE
INSERT INTO StoredTimestamps (Name, Timestamp) VALUES (timestampKey, NOW());
END IF;
END IF;
END IF;
END
To me, this is a lot of conditional logic and makes this procedure very verbose. Does anyone know a way this logic can be simplified?
You want INSERT ON DUPLICATE KEY UPDATE:
INSERT INTO StoredTimestamps (Name, Timestamp)
VALUES (timestampKey, COALESCE(timestampValue, NOW()) )
ON DUPLICATE KEY UPDATE Timestamp = VALUES(timestampValue);
The ON DUPLICATE KEY sets the Timestamp to the value being inserted, when the Name already exists.
For this to work, you need a unique index/constraint on Name:
ALTER TABLE StoredTimestamps ADD CONSTRAINT unq_StoredTimestamps_Name UNIQUE (Name);
In a stored procedure, I would be careful about naming conventions as well:
CREATE PROCEDURE usp_AddUpdateTimestamp (
IN in_timestampKey VARCHAR(50),
IN in_timestampValue TIMESTAMP)
)
BEGIN
INSERT INTO StoredTimestamps (Name, Timestamp)
VALUES (in_timestampKey, COALESCE(in_timestampValue, NOW()))
ON DUPLICATE KEY UPDATE Timestamp = VALUES(timestampValue)
END;
I prefer usp_ rather than sp_ due to my history of using SQL Server, where sp_ is discouraged because it is used for system stored procedures. That is simply a habit and not really relevant to MySQL.
Related
I have a mysql table where I use this query:
INSERT INTO `stats` (`id`, `shop`, `price`, `timestamp`)
VALUES (NULL, '$shop', '$price', 'timestamp') ON DUPLICATE KEY UPDATE price='$price'
The shop column is unique. "Id" = primary key. The timestamp column is updated by mysql: on update CURRENT_TIMESTAMP
Data in the dB:
row: id=1, shop=viacom, price=5, timestamp=1524183480
Case 1: Row to be inserted: shop=viacom, price=6
Result: The existing row is updated
Case 2: Row to be inserted: shop=viacom, price=5 (<-- price has NOT changed)
Result: The existing row is NOT updated
I would like to get case 2 working. I can handle it with php-code, but I'd rather let Mysql do that job. Any ideas? (I tried adding a Where Clause like $shop=shop)
Since the shop column is a UNIQUE key, you can remove the id column and use the below.
replace into stats (shop, price) values ('$shop', '$price')
If shop already exists, then the price is updated. Else a new shop will be inserted. Is this what you want?
Try to update the timestamp column manually:
INSERT INTO `stats` (`shop`, `price`) VALUES ('$shop', '$price')
ON DUPLICATE KEY UPDATE price='$price', timestamp = NOW();
=========
Option #2:
If you want to do it in MySQL, create stored procedure and call the stored procedure from PHP code.
CREATE PROCEDURE `createOrUpdatePrice` (ex_shop varchar(255),ex_price int(11))
BEGIN
declare occures tinyint(1);
SELECT COUNT(`shop`) into occures from `stats` WHERE shop = ex_shop;
IF occures = 0 Then
INSERT INTO `stats` (`id`, `shop`, `price`) VALUES (NULL, ex_shop, ex_price);
ELSE
UPDATE `stats` SET price = ex_price where shop = ex_shop;
END IF;
END
I create a script/workflow exportation/importation from 2 system.
I have Table1 {id, name, description}
I want to create a script (not a procedure). I could (I didnt succed) adding procedure into my workflow. (create and delete at the end)
id is auto increment
I cant change the table
I can be sure that between the time I start execution of my script and the end, there will not be an insertion of one of my items into the database.
The script insert {name,description} but I want to NOT insert if the element (name or name and description) is there.
BASE QUERY :
INSERT INTO TABLE1 (name,description) VALUES ('itemX','this is item X')
BASE Script :
Use database1;
BEGIN;
SELECT * FROM TABLE1 ;
SELECT * FROM TABLE3 ;
INSERT INTO TABLE1 (name,description) VALUES ('itemX','this is item X');
set #idTable1 = LAST_INSERT_ID();
INSERT INTO TABLE3 (idTable1,idTable2) VALUES (#idTable1,1);
INSERT INTO TABLE3 (idTable1,idTable2) VALUES (#idTable1,2);
SELECT * FROM TABLE1 ;
SELECT * FROM TABLE3 ;
ROLLBACK;
I want to protect the multiple insertion on TABLE1. But without changing the table.
Maybe I did it wrong
I tried IF but not working outside procedure.
I tried IGNORE (valid only if id is the same, but never the same, its
auto increment)
I tried WHEN
I tried ON DUPLICATE KEY
Because of #idTable1, I will need change the " set #idTable1 = LAST_INSERT_ID();" if I doesnt have if else. But if my item is the only one with the same "name", I can get this instead of last_insert_id.
I opted for creating procedure before my "BEGIN" and removed them at the end of the script.
Just create the table with name as primary key, then be sure that you take care of the key capitalization (uppercase or lowercase) to avoid duplicates.
CREATE TABLE TABLE1(
id INT AUTO_INCREMENT,
name CHAR(30),
description CHAR(100),
PRIMARY KEY (name)
)
create unique constraint on name field if possible.
Otherwise, create trigger before insert in order to ignore duplicate insertion.
Trigger for checking duplicate on two fields a and b:
delimiter //
drop trigger if exists aborting_trigger //
create trigger aborting_trigger before insert on t
for each row
begin
set #found := false;
select true into #found from t where a=new.a and b=new.b;
if #found then
signal sqlstate '45000' set message_text = 'duplicate insert';
end if;
end //
delimiter ;
The trigger here provides feature similar to unique constraint. After creation you should use INSERT IGNORE or INSERT ...ON DUPLICATE KEY UPDATE
So I have a trigger that works on update. Totally works fine.
Insert in cars(date, id, parent_id) values (date, ford, 2)
What I need to do is to actually check to see if the parent_id already exists. If it does do nothing but if it does not exist then do the insert statement.
right now i have
SET #myVar1 = (SELECT parent_id from cars where parent_id = NEW.id);
IF #myVar1 = NULL;
Insert in cars(date, id, parent_id) values (date, ford, 2);
ENDIF;
I keep getting sysntax error. How am I writing this worng?
The problem is on this line:
Insert in cars(date, id, parent_id) values (date, ford, 2);
The in should be INTO. That's the syntax error.
That said, you might be better served with an INSERT...ON DUPLICATE KEY or REPLACE INTO statement rather than an on-update trigger. Be careful with REPLACE INTO though, as it can be dangerous (but the danger can be somewhat mitigated by using transactions).
dunno if this what you really need. but you can try this one
SET #myVar1 = (SELECT parent_id from cars where parent_id = NEW.id);
IF (#myVar1 is NULL) then
Insert into cars(`date`, id, parent_id) values (date(), new.`name`, new.id);
END IF;
or
Insert into cars(`date`, id, parent_id) values (date(), new.`name`, new.id) on duplicate key update `date`=date();
on mysql must be "end if" not "endif".
new.name is assumes that id field on car from trigger table
you can use on duplicate key update if cars table use primary key or unique key like mention above
and if you doesn't want to change any record if exists then after key update change to id=id or you can use any field.
Let’s consider table
Video(
IDvideo(PK),
Date,
Description,
User
)
with mysql I have no way of writing assertions.
Is it possible to simulate the following assertion using one or more triggers ?
create assertion asser1
check (0 =
( select count(*)
from Video
where Date >= DATE_SUB(current_date(),INTERVAL 1 YEAR )
&& Date<=current_date()
group by User
having count(*) > 200
)
)
how should I write that trigger?
Well, the problem is that MySQL doesn't have an equivalent of a STOP ACTION command. So basically, the work arounds are quite dirty:
One way is that you can violate a constraint inside the trigger to bubble an error and cancel the insert:
CREATE TABLE stop_action (
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(35),
UNIQUE KEY (id, name)
);
INSERT INTO stop_action (1, 'Assert Failure');
Then, in the trigger, just try to:
INSERT INTO stop_action (1, 'Assert Failure');
The benefit of that, is that the error that's returned will be a duplicate key error, and the text will include "Assert Failure".
So then your trigger would become:
delimiter |
CREATE TRIGGER asser1_before BEFORE INSERT ON test1
FOR EACH ROW BEGIN
SELECT count(*) INTO test FROM (select count(*)
from Video
where Date >= DATE_SUB(current_date(),INTERVAL 1 YEAR )
&& Date<=current_date()
group by User
having count(*) > 200);
IF test != 0 THEN
INSERT INTO stop_action (1, 'Assert Failure');
END IF;
END;
|
delimiter ;
Now, you'd need to do this before UPDATE as well, otherwise you could update the date into an invalid state. But otherwise, that should at least get you started...
Is there a way to actually get the column name that was updated in order to use it in a trigger?
Basically I'm trying to have an audit trail whenever a user inserts or updates a table (in this case it has to do with a Contact table)
CREATE TRIGGER `after_update_contact`
AFTER UPDATE ON `contact` FOR EACH ROW
BEGIN
INSERT INTO user_audit (id_user, even_date, table_name, record_id, field_name, old_value, new_value)
VALUES (NEW.updatedby, NEW.lastUpdate, 'contact', NEW.id_contact, [...])
END
How can I get the name of the column that's been updated and from that get the OLD and NEW values of that column. If multiple columns have been updated in a row or even multiple rows would it be possible to have an audit for each update?
Just use OLD.colname <> NEW.colname for each column to check and find those that are different. Triggers are a bit limited in their use in MySQL, too bad.
Try this code...
create table sales (
orderno INT,
sale INT,
empsalary int,
ts TIMESTAMP
);
create table history (
updated varchar(20),
oldvalue INT,
newvalue INT
);
INSERT INTO sales
(orderno, sale, empsalary)
VALUES
(1,700,7000),
(2,800,8000),
(3,900,9000);
DROP TRIGGER test.instrigger;
DELIMITER ///
CREATE TRIGGER test.instrigger
AFTER UPDATE ON sales
FOR EACH ROW
BEGIN
IF NEW.sale <> OLD.sale THEN
INSERT INTO history
(updated, oldvalue, newvalue)
VALUES
('sale', OLD.sale,NEW.sale);
END IF;
IF NEW.empsalary <> OLD.empsalary THEN
INSERT INTO history
(updated, oldvalue, newvalue)
VALUES
('empsalary', OLD.empsalary,NEW.empsalary);
END IF;
END;
///
DELIMITER ;