Everywhere I look it seems MySQL stored procedures can do transactions. Yet when I declare my stored function
create function test( a int )
returns int
MODIFIES SQL DATA
BEGIN
START TRANSACTION ;
update t set col='some value' where id=a ;
COMMIT ;
return 0 ;
END //
I get
Error Code: 1422. Explicit or implicit commit is not allowed in stored function or trigger.
asked Jun 6, 2013 at 18:49
Actually you are not allowed transactions inside stored functions. You are allowed transactions inside stored procedures only.
create procedure test( a int )
MODIFIES SQL DATA
BEGIN
START TRANSACTION ;
update t set col='some value' where id=a ;
COMMIT ;
END //
To return values from the SP, use output parameters or use the result set from the last select statement in the SP.
answered Jun 6, 2013 at 18:49
bobobobobobobobo
63.7k60 gold badges254 silver badges355 bronze badges
2
I have Created a trigger, where I am storing some specific data in a table and after the trigger gets executed I am deleting that Table.
The query :
CREATE TRIGGER `afterinsert_Golden_Ball` AFTER INSERT ON `team_trophies` FOR EACH ROW
BEGIN
DECLARE badgeID INT(10);
CREATE TABLE tempname AS
(SELECT AVG(d.fair_play) as AvgPlay , b.tournament_id , d.* FROM
`team_trophies` a INNER JOIN tournament_scheduling b ON
a.tournament_id = b.tournament_id INNER JOIN matches c ON b.id
= .match_id INNER JOIN user_match_result d ON c.id = d.match_id WHERE
a.tournament_id = 1 GROUP BY d.user_id);
IF EXISTS (SELECT * from tempname WHERE AvgPlay = (SELECT MAX(AvgPlay) FROM tempname))
THEN
SELECT badge_id INTO badgeId FROM badge_master WHERE name = 'Golden Ball';
Insert into user_badges(badges_id , user_id , match_id , turf_id ,
tournament_id , created_date)
SELECT badgeID , user_id , match_id , NULL , tournament_id ,
created_date from tempname limit 1;
END IF;
DROP TABLE tempname;
END
MySQL Error : 1422 Explicit and Implicit Commit is not allowed in SP and Trigger
Kindly Help.
asked Mar 17, 2017 at 13:03
(Too much for a Comment…) (Working backwards…)
LIMIT 1
, but no ORDER BY
?? You don’t care which item you get?
SELECT .. INTO badgeId
, but then don’t really use it? Perhaps the subsequent SELECT
should have WHERE name = 'Golden Ball'
?
Instead of doing IF EXISTS
, try to cause the final SELECT
to return nothing. That would have the same effect, and will probably eliminate the ‘need’ for a temp table.
Does that IF EXISTS
do anything other then see if there are any rows in tempname
?? If so please explain.
Is d.user_id
the PRIMARY KEY
of team_trophies
? If not, the GROUP BY
may be improper.
No mention of OLD.
or NEW.
??
I suspect the INSERT .. SELECT
could be simply use the original SELECT
, plus an additional WHERE
clause based ‘Golden Ball’, but I am too lost to attempt to rewrite it.
answered Mar 18, 2017 at 15:12
Rick JamesRick James
72.9k4 gold badges41 silver badges101 bronze badges
This is a trigger, which is same as function could be called multiply time at unpredicted for You time
so the code like
CREATE TABLE tempname;
DROP TABLE tempname;
illegal for this type of code.
Just think what would be if 2nd session start insert at the same time? (100% realistic situation in any multi user environment)
For prevent collisions — this is not allowed.
answered Mar 19, 2017 at 0:45
a_vlada_vlad
3,5572 gold badges11 silver badges16 bronze badges
1
Я пытаюсь создать триггер before_insert на случай, если два столбца имеют значение NULL, чтобы не допускать вставку данных. Идея, которую я пытался применить, заключалась в том, чтобы создать ограничение notnull для обоих столбцов во время вставки, а затем удалить это ограничение.
Я не очень разбираюсь в SQL и попробовал использовать следующий код:
CREATE DEFINER = CURRENT_USER TRIGGER `db_autoescola`.`contas_recebers_BEFORE_INSERT` BEFORE INSERT ON `contas_recebers` FOR EACH ROW
BEGIN
IF NEW.id_recepcionista IS NULL and NEW.id_instrutor IS NULL
THEN ALTER TABLE contas_recebers MODIFY id_recepcionista INT NOT NULL;
ALTER TABLE contas_recebers MODIFY id_instrutor INT NOT NULL;
END IF;
END
Однако при попытке применить этот триггер у меня возникла следующая ошибка, и я не смог определить, что необходимо для ее исправления.
Operation failed: There was an error while applying the SQL script to the database.
Executing:
DROP TRIGGER IF EXISTS `db_autoescola`.`contas_recebers_BEFORE_INSERT`;
DELIMITER $$
USE `db_autoescola`$$
CREATE DEFINER = CURRENT_USER TRIGGER `db_autoescola`.`contas_recebers_BEFORE_INSERT` BEFORE INSERT ON `contas_recebers` FOR EACH ROW
BEGIN
IF NEW.id_recepcionista IS NULL and NEW.id_instrutor IS NULL
THEN ALTER TABLE contas_recebers MODIFY id_recepcionista INT NOT NULL;
ALTER TABLE contas_recebers MODIFY id_instrutor INT NOT NULL;
END IF;
END$$
DELIMITER ;
ERROR 1422: Explicit or implicit commit is not allowed in stored function or trigger.
SQL Statement:
CREATE DEFINER = CURRENT_USER TRIGGER `db_autoescola`.`contas_recebers_BEFORE_INSERT` BEFORE INSERT ON `contas_recebers` FOR EACH ROW
BEGIN
IF NEW.id_recepcionista IS NULL and NEW.id_instrutor IS NULL
THEN ALTER TABLE contas_recebers MODIFY id_recepcionista INT NOT NULL;
ALTER TABLE contas_recebers MODIFY id_instrutor INT NOT NULL;
END IF;
END
2 ответа
Лучший ответ
Использование DDL (языка определения данных) в качестве
ALTER TABLE
Производить неявную фиксацию, а это не допускается в триггере. Вы не можете изменить среду выполнения структуры данных, эти операции должны быть выполнены до того, как вы начнете манипулировать данными.
1
Braiam
12 Фев 2021 в 14:18
Спасибо @scaisEdge, помня об этом, я смог придумать решение:
Вместо того, чтобы пытаться изменить таблицу, я создал столбец notnull, чтобы проверить, когда они равны нулю, таким образом я передам ему нулевое значение, если оно истинно, или любое другое значение в противном случае. Таким образом я могу получить исключение в своем приложении и обработать его так, как я хочу.
Код:
CREATE DEFINER = CURRENT_USER TRIGGER `contas_recebers_BEFORE_INSERT` BEFORE INSERT ON `contas_recebers` FOR EACH ROW BEGIN
IF NEW.id_recepcionista IS NULL and NEW.id_instrutor IS NULL
THEN SET NEW.ids_null = null;
else SET NEW.ids_null = TRUE;
END IF;
END
Я имею в виду, что это может быть даже глупая проблема и решение не из лучших, но это то, как мне удалось ее решить, может быть, это поможет кому-то другому.
0
RafaelTI
12 Фев 2021 в 14:46
Question:
I’m trying to create a trigger that inserts data into the “contador” column of the ATRACCIONES table, and first checks if that column exists and if not creates it.
But I get the following error that I don’t know how to fix:
Error Code: 1422. Explicit or implicit commit is not allowed in stored function or trigger.
USE CIRCO;
DROP TRIGGER IF EXISTS atracciones_numAtracc_INSERT;
DELIMITER $$
CREATE TRIGGER atracciones_numAtracc_INSERT AFTER INSERT ON ATRACCION_DIA FOR EACH ROW
BEGIN
DECLARE v_col varchar(20);
SELECT COLUMN_NAME
INTO v_col
FROM INFORMATION_SCHEMA.COLUMNS
WHERE COLUMN_NAME = 'contador' AND TABLE_NAME = 'ATRACCIONES';
IF (v_col IS NULL) THEN
ALTER TABLE ATRACCIONES ADD `contador` int NOT NULL DEFAULT 0;
END IF;
UPDATE ATRACCIONES
SET contador = contador + 1
WHERE nombre = NEW.nombre_atraccion;
END $$
DELIMITER ;
Answer:
Just do the update and keep the trigger simple. Don’t try to check in the trigger for columns that don’t exist.
CREATE TRIGGER atracciones_numAtracc_INSERT AFTER INSERT ON ATRACCION_DIA FOR EACH ROW
BEGIN
UPDATE ATRACCIONES
SET contador = contador + 1
WHERE nombre = NEW.nombre_atraccion;
END $$
Write the trigger with an assumption that the columns you need do exist.
Before you insert data, make sure the tables do contain the needed columns. It is a common practice to apply ALTER TABLE statements to your tables as you deploy a new version of your application. Then you only need to check once.
Checking for the missing column every time you insert a row of data, which could happen millions of times, is an unnecessary and wasteful action.
If you have better answer, please add a comment about this, thank you!
Somebody asked about the possibility of putting DML statements inside MySQL stored functions. DML statements like the INSERT
, UPDATE
, and DELETE
. When I said, “Yes, you can put DML statements inside functions.” They showed me the error they encountered, which is only raised at compilation when you put an explicit COMMIT
statement or a Data Definition Language (DDL) statement (CREATE
, ALTER
, DROP
, or RENAME
) inside a MySQL function. The actual error message displayed is:
ERROR 1422 (HY000): Explicit OR implicit commit IS NOT allowed IN stored FUNCTION OR TRIGGER.
While an explicit COMMIT
is obvious when placed inside a function, the implicit COMMIT
statement isn’t obvious unless you know a DDL statement generates one. This means you can’t include any DDL statement inside a stored function.
The following example shows how a DDL statement creates an immediate implicit COMMIT
. It requires two sessions and Transaction Control Language (TCL) statements.
In session one, create a table, start a transaction scope, and insert one row into the table:
-- Create a table. CREATE TABLE message ( message_id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT , message_text VARCHAR(20)); -- Start a transaction context. BEGIN WORK; -- Insert a row into the MESSAGE table. INSERT INTO message (message_text) VALUES ('Implicit Commit?');
In session two, the empty set is displayed when you query the MESSAGE
table. Returning to session one, add a new column to the MESSAGE
table with this statement:
ALTER TABLE message ADD (sent DATETIME);
The ALTER
statement automatically makes current any pending data changes, effectively committing the record from session one. You can return to the second session and query the table you get the following results:
+------------+------------------+------+ | message_id | message_text | sent | +------------+------------------+------+ | 1 | Implicit Commit? | NULL | +------------+------------------+------+
When you embed DML statements inside functions, they rely on automatic commit behaviors or an external transaction scope. This function compiles and returns 1 when successful and 0 when unsuccessful, which effectively controls all error conditions within the function:
CREATE FUNCTION modifies_data ( pv_message CHAR(20) ) RETURNS INT MODIFIES SQL DATA BEGIN /* Declare Boolean-like variables as FALSE. */ DECLARE lv_return_value INT DEFAULT FALSE; DECLARE lv_error_value INT DEFAULT FALSE; /* Declare a generic exit handler to reset error control variable to true. */ DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SET lv_error_value := TRUE; /* Insert statement with auto commit enabled. */ INSERT INTO message (message_text) VALUES (pv_message); /* True unless the CONTINUE HANDLER disables the error control variable. */ IF lv_error_value = FALSE THEN SET lv_return_value := TRUE; END IF; /* Return local variable. */ RETURN lv_return_value; END; $$
With auto commit enabled, any call to the function in a query writes a row to the table. If you start a transaction and in the scope of the transaction test the function in one session and query the table in another you’ll see that transaction control can be managed outside the function. You can also manage the transaction control inside a stored procedure, which holds all the TCL commands. An example follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
CREATE PROCEDURE test_modifies_data ( pv_message CHAR(20) ) MODIFIES SQL DATA BEGIN /* Declare a control variable that manages transaction success or failure. */ DECLARE lv_success_value INT DEFAULT FALSE; /* Start a transaction context. */ START TRANSACTION; /* Set a SAVEPOINT in the transaction context. */ SAVEPOINT before_transaction; /* Call the function. */ SET lv_success_value := modifies_data(pv_message); /* Check the status of the control variable, and commit or rollback the transaction. */ IF lv_success_value = TRUE THEN COMMIT; ELSE ROLLBACK TO before_transaction; END IF; END; $$ |
Test the two with the following call to the procedure:
CALL test_modifies_data('Agent Coulson');
Answers my question and I hope it helps others.
-
#1
Ошибка 1422, триггер
PHP:
DELIMITER |
CREATE
TRIGGER trigger_catalog_content_fk_upd BEFORE UPDATE
ON modules_content_catalog_fk FOR EACH ROW
BEGIN
IF New.fk_catalog_parent = New.fk_catalog THEN
SET New.fk_catalog_parent = 0;
ELSEIF New.fk_catalog_parent IN (SELECT fk_catalog FROM modules_content_catalog_fk WHERE fk_catalog_parent = New.fk_catalog) THEN
ROLLBACK;
END IF;
END;
| DELIMITER;
появляется ошибка:
Script line: 32 Explicit or implicit commit is not allowed in stored function or trigger.
обратите свой взгляд на
PHP:
ELSEIF New.fk_catalog_parent IN (SELECT fk_catalog FROM modules_content_catalog_fk WHERE fk_catalog_parent = New.fk_catalog) THEN
ROLLBACK;
END IF;
если новый родительский каталог вход в список каталогов, у которых родительский каталог заменяемый каталог то откатить такое действие…
т.е. чтобы не было связи
3=>4
4=>3
и каталог не утанул +)
Dl
Новичок
-
#2
The trigger cannot use statements that explicitly or implicitly begin or end a transaction such as START TRANSACTION, COMMIT, or ROLLBACK.
Может просто New.fk_catalog_parent=Old.fk_catalog_parent ?
-
#3
нет,
есть такая запись
fk_c | fk_c_parent
3 | 0
4 | 3
5 | 3
и если вдруг для fk_c = 3 назначается fk_c_parent = 5
то тригер не принимал эту запись…
т.к. fk_c = 5 имеет родительский каталог fk_c_parent = 3
PHP:
SELECT fk_catalog FROM modules_content_catalog_fk WHERE fk_catalog_parent = New.fk_catalog
возвратит все записи
4
5
т.е. если fk_c_parent будет входить в эту группу, то откатить манипуляцию над записью
-~{}~ 15.08.08 10:56:
————————
я изминил логику триггер, теперь работает…
PHP:
DELIMITER |
CREATE
TRIGGER trigger_catalog_content_fk_upd BEFORE UPDATE
ON modules_content_catalog_fk FOR EACH ROW
BEGIN
SET @x = (SELECT fk_catalog_parent FROM modules_content_catalog_fk WHERE fk_catalog = New.fk_catalog_parent);
IF New.fk_catalog_parent = New.fk_catalog THEN
SET New.fk_catalog_parent = 0;
END IF;
IF New.fk_catalog = @x THEN
SET New.fk_catalog_parent = old.fk_catalog_parent;
END IF;
END;
| DELIMITER;
Dl я тебя теперь понял!
Dl
Новичок
-
#4
Ок)
-~{}~ 15.08.08 11:15:
Только на мой взгляд такую проверку лучше делать не в триггере, чтобы можно было вывести пользователю сообщение об ошибке.
-
#5
диспечеру я на пальцах объясню что можно делать а что нельзя и что будет без результатно… просто времени очень не хватает на продумку всего
Я создаю «банк» в качестве задания для курса базы данных, который я беру. Я создал хранимую функцию, которая принимает несколько переменных IN
, таких как идентификатор учетной записи, идентификатор клиента и PIN-код, и проверяет это, чтобы проверить, действительны ли представленные данные. Если данные действительны, процедура обновляет остаток на счете для представления денежной транзакции. Затем он «возвращает», действительно ли представленные данные были действительными. Вот код процедуры:
DELIMITER //
CREATE PROCEDURE retrieveMoney (
IN holder INT,
IN pin VARCHAR(4),
IN account INT,
IN amount FLOAT,
OUT success INT
)
BEGIN
START TRANSACTION;
SELECT COUNT(id) INTO success FROM account_holder WHERE id=holder AND pin=pin;
IF success IS NOT NULL THEN
IF (SELECT balance-amount FROM account WHERE id=account) >= 0 THEN
UPDATE account SET balance = balance-amount WHERE id=account;
CALL logTransaction(account,NULL,amount);
COMMIT;
ELSE ROLLBACK;
END IF;
ELSE ROLLBACK;
END IF;
END//
DELIMITER ;
Я хотел облегчить просмотр вывода этой процедуры, и поскольку мне не разрешено использовать транзакции внутри функции, я решил написать функцию обертки следующим образом:
DELIMITER //
CREATE FUNCTION retrieveMoney (
holder INT,
pin VARCHAR(4),
account INT,
amount FLOAT
)
RETURNS INT
BEGIN
CALL retrieveMoney(holder,pin,account,amount,@success);
RETURN @success;
END//
DELIMITER ;
К сожалению, это не работает, и я до сих пор получаю следующую ошибку:
ERROR 1422 (HY000): Explicit or implicit commit is not allowed in stored function or trigger.
Это потому, что я вызываю процедуру, содержащую транзакции, внутри функции?