The table tbtable contains the following columns.
The procedure to create or update an entry in tbtable is the following.
CREATE PROCEDURE `createOrUpdateTbTable` (
IN `this_pid` INT UNSIGNED,
IN `this_sid` INT UNSIGNED,
IN `this_ri` LONGBLOB,
IN `this_defaults` TINYINT,
IN `this_approved` TINYINT,
OUT `id` INT UNSIGNED
)
BEGIN
UPDATE `tbtable` SET
`ri` = this_ri, `defaults` = this_defaults, `approved` = this_approved
WHERE `pid` = this_pid AND `sid` = this_sid;
IF ROW_COUNT() = 0
THEN
INSERT INTO `tbtable` (`pid`, `sid`, `ri`, `defaults`, `approved`)
VALUES (this_pid, this_sid, this_ri, this_defaults, this_approved);
SET id = LAST_INSERT_ID();
END IF;
END
Right now I don't have any way to get the id of an entry when an update occurs. To what script should I change my current createOrUpdate method so that I can also retrieve the id when an update happens?
I checked other similar questions but they don't have any OUT parameter, so not applicable for my case.
Thanks.
EDIT:
BEGIN
IF EXISTS (SELECT*FROM `tbtable` WHERE `pid` = this_pid AND `sid` = this_sid)
THEN
UPDATE `tbtable`
SET
`ri` = this_ri, `defaults` = this_defaults, `approved` = this_approved
WHERE `pid` = this_pid AND `sid` = this_sid;
SET id = `id` ;
ELSE
INSERT INTO `tbtable` (`pid`, `sid`, `ri`, `defaults`, `approved`)
VALUES (this_pid, this_sid, this_ri, this_defaults, this_approved);
SET id = LAST_INSERT_ID();
END IF;
END
I tried this approach as well, but the id is null when there is an update.
We could run a SELECT t.myid INTO v_id FROM t WHERE ... statement to store a value into a local procedure variable.
Or, we could set a user-defined variable.
Note that the same identifier might be used for a routine parameter, a local variable and a column. A routine parameter takes precedence over a table column.
In the general case, an UPDATE statement can affect more than one row, so we could have multiple rows. The procedure argument is a scalar, so we would need to decide which of the rows we want to return the id from.
Assuming that id column is guaranteed to be non-NULL in the (unfortunately named) tbtable table...
BEGIN
DECLARE lv_id BIGINT DEFAULT NULL;
-- test if row(s) exist, and fetch lowest id value of from matching rows
SELECT t.id
INTO lv_id -- save retrieved id value into procedure variable
FROM tbtable t
WHERE t.pid = this_pid
AND t.sid = this_sid
ORDER BY t.id
LIMIT 1
;
-- if we got a non-NULL value returned
IF lv_id IS NOT NULL THEN
-- do the update
UPDATE `tbtable` t
SET t.ri = this_ri
, t.defaults = this_defaults
, t.approved = this_approved
WHERE t.pid = this_pid
AND t.sid = this_sid
;
ELSE
INSERT INTO `tbtable` (`pid`, `sid`, `ri`, `defaults`, `approved`)
VALUES (this_pid, this_sid, this_ri, this_defaults, this_approved)
;
SET lv_id = LAST_INSERT_ID();
END IF;
-- set OUT parameter
SET id = lv_id ;
END$$
Note that this procedure is subject to a race condition, with a simultaneous DELETE operation from another session. Our SELECT statement could return an id for a matching row, and another session could DELETE that row, and then our update runs, and doesn't find the row. Timing here is pretty tight, it would be difficult to demonstrate this without adding a delay into the procedure, like a SELECT WAIT(15); right before the UPDATE (to give us fifteen seconds to run a delete from another session.)
You try to return a single value but your update statement could be executed in multiple rows. So when you return the id from that type of updated statement , you need to loop through the updated rows and return any one of those updated row values (because you expect that the combination of pid and sid is unique). Here is sample code without the rid columns as i do not want to create a temporary database with that :)
CREATE PROCEDURE createOrUpdateTbTable (
IN this_pid INT UNSIGNED,
IN this_sid INT UNSIGNED,
IN this_ri LONGBLOB,
IN this_defaults TINYINT,
IN this_approved TINYINT,
OUT id INT UNSIGNED
)
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE updated_id INT;
DECLARE updatedIds CURSOR FOR SELECT tbtable.id FROM tbtableWHERE pid = this_pid AND sid = this_sid;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
IF EXISTS (SELECT*FROM `tbtable` WHERE `pid` = this_pid AND `sid` = this_sid)
THEN
UPDATE `tbtable`
SET
`defaults` = this_defaults, `approved` = this_approved
WHERE `pid` = this_pid AND `sid` = this_sid;
OPEN updatedIds;
read_loop: LOOP
FETCH updatedIds INTO updated_id;
SET id = updated_id;
IF done THEN
LEAVE read_loop;
END IF;
END LOOP;
CLOSE updatedIds;
ELSE
INSERT INTO `tbtable` (`pid`, `sid`, `defaults`, `approved`)
VALUES (this_pid, this_sid, this_defaults, this_approved);
SET id = LAST_INSERT_ID();
END IF;END
You need explicit return the value at the end:
IF ROW_COUNT() = 0
THEN
INSERT INTO `tbplanhassurface` (`planid`, `surfaceid`, `roi`, `defaultsurface`, `approved`)
VALUES (this_planid, this_surfaceid, this_roi, this_defaultsurface, this_approved);
SET id = LAST_INSERT_ID();
ELSE
SELECT #id = your_id_field
FROM `tbplanhassurface`
WHERE `planid` = this_planid
AND `surfaceid` = this_surfaceid;
END IF;
SELECT #id;
END
I am trying to Create Trigger in MySQL with Select Column data from Join multiple table. But Trigger is not allow me to DECLARE temp variable.
I would like to join 4 table on the bases of newly inserted record in one table and select the data from different table and insert r update in another table (DashboardStatus)
I am getting error [ SQL Error (1064): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'SET _BedId = (SELECT bd.BedId
FROM LifetouchHeartRate lthr
JOIN Device' at line 4 */ ]
CREATE TABLE `dashboardstatus` (
`LTHR` INT(11) NULL DEFAULT NULL,
`BedId` INT(11) NULL DEFAULT NULL
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB;
CREATE TRIGGER triggerDashboard AFTER INSERT ON LifetouchHeartRate for each row
BEGIN
DECLARE _BedId INT
SET _BedId = (SELECT bd.BedId As _BedId
FROM LifetouchHeartRate lthr
JOIN DeviceSession ds ON ds.DeviceSessionID = lthr.ByDevSessionId
JOIN PatientSession ps ON ps.PatientSessionId = ds.ByPatientSessionId
JOIN PatientDetails pd ON pd.PatientDetailsId = ps.ByPatientId
JOIN BedDetails bd ON bd.BedDetailsId = pd.ByBedId
WHERE lthr.LifeTouchHeartRateID = new.LifeTouchHeartRateID Limit 1 );
IF _BedId > 0
BEGIN
INSERT OR REPLACE INTO DashboardStatus (LTHR, BedId) VALUES ( new.LifeTouchHeartRateID, _BedId)
END
END
Remember ;
...
CREATE TRIGGER triggerDashboard AFTER INSERT ON LifetouchHeartRate for each row
BEGIN
-- DECLARE _BedId INT
DECLARE _BedId INT;
...
UPDATE
DELIMITER //
CREATE TRIGGER triggerDashboard AFTER INSERT ON LifetouchHeartRate
FOR EACH ROW
BEGIN
-- DECLARE _BedId INT
DECLARE _BedId INT;
SET _BedId = (SELECT bd.BedId As _BedId
FROM LifetouchHeartRate lthr
JOIN DeviceSession ds ON ds.DeviceSessionID = lthr.ByDevSessionId
JOIN PatientSession ps ON ps.PatientSessionId = ds.ByPatientSessionId
JOIN PatientDetails pd ON pd.PatientDetailsId = ps.ByPatientId
JOIN BedDetails bd ON bd.BedDetailsId = pd.ByBedId
WHERE lthr.LifeTouchHeartRateID = new.LifeTouchHeartRateID Limit 1 );
/*IF _BedId > 0
BEGIN
INSERT OR REPLACE INTO DashboardStatus (LTHR, BedId) VALUES ( new.LifeTouchHeartRateID, _BedId)
END
END*/
IF _BedId > 0 THEN
-- BEGIN
INSERT INTO DashboardStatus (LTHR, BedId) VALUES ( new.LifeTouchHeartRateID, _BedId);
-- END
END IF;
END//
DELIMITER ;
SQL Fiddle demo
I have a table that looks something like this:
Columns:
user_id int(11) PK
module_id int(11) PK
academy_team_id int(11) PK
academy_id int(11) PK
sort_number int(11)
is_complete int(11)
score_to_pass int(11)
is_open int(11)
Now i wish to add a trigger so that when you update this table if the value is_complete is equal to 1 then update the next row's is_open and set it to1
I have attempted with the following trigger sql:
begin
if new.is_complete = 1 then
set next.is_open = 1;
end if ;
end
Sadly this did not work so im not sure how to do it can anyone push me in the right direction?
According to pala_ Answer
im getting the following error when updating my row:
ERROR 1442: 1442: Can't update table 'user_has_academy_module' in stored function/trigger because it is already used by statement which invoked this stored function/trigger.
SQL Statement:
UPDATE `system`.`user_has_academy_module` SET `is_complete`='1' WHERE `user_id`='1' and`module_id`='11' and`academy_team_id`='49' and`academy_id`='29'
Your basic trigger body should be something like this:
begin
if new.is_complete = 1 and (select id from <table> where user_id = new.user_id and module_id = new.module_id and academy_team_id = new.academy_team_id sort_number = new.sort_number +1 ) then
update <table> set is_open = 1
where user_id = new.user_id
and academy_team_id = new.academy_team_id
and module_id = new.module_id
and sort_number = new.sort_number + 1;
end if
end
It will check to see if there IS another thing to set open (based on same user_id, academy_team_id and module_id, and next sequential sort_number), and if there is, set it open.
MySQL cant update the same table the trigger is set on. It will need to be done with a stored procedure instead.
delimiter //
create procedure completeandopen(IN param INT)
begin
declare next_id integer;
declare _user_id integer;
declare _module_id integer;
declare _academy_team_id integer;
declare _sort_number integer;
select user_id,
module_id,
academy_team_id,
sort_number
into _user_id,
_module_id,
_academy_team_id,
_sort_number
from tester
where id = param;
update tester set is_complete = 1 where id = param;
select id
into next_id
from tester
where id = param + 1
and user_id = _user_id
and module_id = _module_id
and academy_team_id = _academy_team_id
and sort_number = _sort_number + 1;
if (next_id is not null) then
update tester set is_open = 1 where id = next_id;
end if;
end//
delimiter ;
I think this should work - i haven't tested on your table structure, and it does assume a unique primary key on your table. If it doesn't have that - it's easy enough to modify.
To use it, just call completeandopen(id of the row to be completed) (after changing the table name from tester to your table name)
I am trying to set a variable in a trigger that is the most recent entry in the table. However, PHPMyAdmin notes there is an error at line SET clicked_campaign_id =. I do not see the problem here.
CREATE TRIGGER tr_user_action_click
AFTER INSERT ON users_click FOR EACH ROW
BEGIN
DECLARE clicked_campaign_id int
SET clicked_campaign_id =
(SELECT campaignId
FROM users_click
WHERE id = (SELECT max(id) FROM users_click));
Update onlineportal.`campaigns`
SET `clicks` = `clicks` + 1
WHERE id = clicked_campaign_id;
END
Is there a different way to set a variable...?
You must set the DELIMITER in phpMyAdmin, as shown in the image:
Then, create the trigger:
CREATE TRIGGER `tr_user_action_click` AFTER INSERT ON `users_click`
FOR EACH ROW
BEGIN
DECLARE `clicked_campaign_id` INT;
SET `clicked_campaign_id` =
(SELECT `campaignId`
FROM `users_click`
WHERE `id` = (SELECT max(`id`) FROM `users_click`));
UPDATE `onlineportal`.`campaigns`
SET `clicks` = `clicks` + 1
WHERE `id` = `clicked_campaign_id`;
END//
When trying to run the following stored procedure from django, I get an OperationError (1172, 'Result consisted of more than one row') Any idea what I might be doing wrong?
-- --------------------------------------------------------------------------------
-- Routine DDL
-- Note: comments before and after the routine body will not be stored by the server
-- --------------------------------------------------------------------------------
DELIMITER $$
CREATE DEFINER=`root`#`localhost` PROCEDURE `UpdatePrices`(IN storeId int, IN bottleSize VARCHAR(50))
BEGIN
DECLARE amount DECIMAL(10,2); DECLARE isCustom INT DEFAULT 0;
DECLARE changeType VARCHAR(50) DEFAULT 'State'; DECLARE updateType INT DEFAULT 0;
IF bottleSize = '1000 Ml' THEN
SELECT S1000IncreaseChoices INTO changeType FROM store_store WHERE StoreID = storeId;
IF changeType = 'State' THEN
SELECT updateType = 0;
END IF;
IF changeType = 'Flat' THEN
SELECT S1000IncreaseAmount INTO amount FROM store_store WHERE StoreID = storeId;
SELECT updateType = 1;
END IF;
IF changeType = 'Percent' THEN
SELECT 1 - S1000IncreaseAmount/100 INTO amount FROM store_store WHERE StoreID = storeId;
SELECT updateType = 2;
END IF;
END IF;
IF updateType = 0 THEN
update store_storeliquor SL
inner join liquor_liquor LL
on liquorID_id = id
set StorePrice = ShelfPrice
where BottleSize = bottleSize
and storeID_id = storeId
and custom = 0;
END IF;
IF updateType = 1 THEN
update store_storeliquor SL
inner join liquor_liquor LL
on liquorID_id = id
set StorePrice = OffPremisePrice + amount
where BottleSize = bottleSize
and storeID_id = storeId
and custom = 0;
END IF;
IF updateType = 1 THEN
update store_storeliquor SL
inner join liquor_liquor LL
on liquorID_id = id
set StorePrice = OffPremisePrice / amount
where BottleSize = bottleSize
and storeID_id = storeId
and custom = 0;
END IF;
END
I'm not sure if it matters, but I initiate the stored procedure like so:
def priceupdate(request, store_id):
cursor = connection.cursor()
cursor.callproc("UpdatePrices", (store_id, '1000 ML'))
cursor.close()
return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
Your SELECT...INTO queries give result sets with more then one record. The WHERE filters are incorrect - they compare two the same values StoreID = storeId. Rename IN storeId int parementer to another name. For example - IN storeId_param int
The query will be like this -
SELECT S1000IncreaseChoices INTO changeType FROM store_store WHERE StoreID = storeId_param;
This is a Bug and you need to apply something like that:
SELECT id,data INTO x,y FROM test.t1 LIMIT 1;