Start Transaction OR Begin Work - mysql

I don't know what is better to use Start Transaction OR Begin Work, and what is the difference.

From the MySQL manual:
BEGIN and BEGIN WORK are supported as aliases of START TRANSACTION for initiating a transaction. START TRANSACTION is standard SQL syntax and is the recommended way to start an ad-hoc transaction.

This is updated answer from MySQL 8.0 manual:
BEGIN ... END syntax is used for writing compound statements, which can appear within stored programs (stored procedures and functions, triggers, and events).
This means that BEGIN ... END syntax is used only to structure your code and to enable nesting (this blocks can be nested).
Later in the manual:
Within all stored programs, the parser treats BEGIN [WORK] as the beginning of a BEGIN ... END block. To begin a transaction in this context, use START TRANSACTION instead.
This means that BEGIN/BEGIN WORK are aliases for BEGIN in BEGIN ... END syntax and they are not aliases from START TRANSACTION. In stored procedures, functions, triggers and events you should always use START TRANSACTION to start a transaction.

Related

What is the perfect strategy to lock tables in MySql?

I have wrote this procedure which creates new A/c voucher. It runs on MySql 5.0. Now, it's time to implement this in our production. But I am not sure that it meets the requirement. Is this Lock Strategy perfect ? Please help.
It calls this way :
CALL SpAcVoucherCreate(1,'2022/03/31','2831',5000,'A001');
My procedure is as follows :
USE `FinanceDB`;
DROP PROCEDURE IF EXISTS `SpAcVoucherCreate`;
DELIMITER $$
CREATE DEFINER=`root`#`localhost` PROCEDURE `SpAcVoucherCreate`(
V_VOCHNO INT,
V_VOCHDT CHAR(10),
V_ACCODE CHAR(4),
V_AMOUNT DECIMAL(12,2),
V_USER_ID CHAR(5)
)
BEGIN
DECLARE V_ERR_OCCURED BOOLEAN;
SET V_ERR_OCCURED=FALSE;
BEGIN
DECLARE EXIT HANDLER FOR SQLEXCEPTION
SET V_ERR_OCCURED=TRUE;
SET AUTOCOMMIT=0;
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
START TRANSACTION;
LOCK TABLES `ACTRANS` WRITE;
INSERT INTO ACTRANS
(VOCHNO,VOCHDT,ACCODE,AMOUNT,USER_ID)
VALUES
(V_VOCHNO,V_VOCHDT,V_ACCODE,V_AMOUNT,V_USER_ID);
IF V_ERR_OCCURED=TRUE THEN
ROLLBACK;
ELSE
COMMIT;
END IF;
UNLOCK TABLES;
SET AUTOCOMMIT=1;
END;
END $$
DELIMITER ;
I find the procedure to be doing several things that are unnecessary.
SERIALIZABLE acts the same as REPEATABLE READ for what you're doing. The only effect of SERIALIZABLE in MySQL is that it makes non-locking SELECT statements into locking SELECT statements as if you had used SELECT...LOCK IN SHARE MODE. Since you only do an INSERT, this difference has no effect.
There's no need to disable autocommit and then do a transaction start and commit/rollback for a single statement. If you had left autocommit enabled, then a successful INSERT would commit, and an unsuccessful INSERT would not commit. It would have the same result as your code.
There would be no need for an SQLEXCEPTION handler to rollback the transaction, because you have only a single statement in the transaction. It either succeeds or it does not succeed.
Regardless of any of the above, you have shown no reason to use LOCK TABLES. It will only block INSERT/UPDATE/DELETE unnecessarily, if multiple clients try to add rows concurrently. If there is a special reason you need to do this, you have not described it.
Your procedure body has an unnecessary BEGIN/END block. It doesn't do anything. Also I believe that DECLARE is only allowed following the first BEGIN, not within other blocks in the body.
There is probably no reason to use a stored procedure at all, since it only accomplishes a single INSERT operation. Why not just do the INSERT directly in the client? I suppose if you want to restrict the privilege to do inserts to the procedure, if the user doesn't have that privilege, that would be a reason.
I also agree with the comment above that you are using a version of MySQL that is suspiciously out of date. MySQL 5.0 passed its end of life date in 2012, according to https://endoflife.software/applications/databases/mysql. You are missing many bug fixes, security patches, and of course modern features.
Update:
The comment from SolarFlare is correct, one is not allowed to use LOCK or UNLOCK statements in stored procedures in MySQL:
mysql> create procedure p()
-> begin
-> lock tables mytable write;
-> insert into mytable () values ();
-> unlock tables;
-> end//
ERROR 1314 (0A000): LOCK is not allowed in stored procedures

MariaDB stored functions

I currently have all of my sql queries written in my PHP files, within each class method. Is it possible to move all of these queries into stored procedures or stored functions in the database & simply pass the corresponding values / arguments into them from PHP?
I have read some of the documentation & it still appears unclear.
Thank you. :)
DELIMITER $$
create procedure `accounting`.`delete_invoice_line` (invoice_line_id INT)
BEGIN
delete from invoice_line where id = invoice_line_id;
END;
$$
DELIMITER ;
I had to figure the format for creating the procedure. I am following this pattern & it appears to be working properly. Then granting execute privileges for the user name.
Thank you all for your input. :)
Most queries can be moved into stored procedures, but probably not all of them. See mariadb's documentation on which SQL statements cannot be used in stored procedures:
ALTER VIEW; you can use CREATE OR REPLACE VIEW instead. LOAD DATA and
LOAD TABLE. INSERT DELAYED is permitted, but the statement is handled
as a regular INSERT.
LOCK TABLES and UNLOCK TABLES.
References to local variables within prepared statements inside a stored routine (use user-defined variables instead).
BEGIN (WORK) is treated as the beginning of a BEGIN END block, not a transaction, so START TRANSACTION needs to be used instead.
The number of permitted
recursive calls is limited to max_sp_recursion_depth. If this variable
is 0 (default), recursivity is disabled. The limit does not apply to
stored functions.
Most statements that are not permitted in prepared
statements are not permitted in stored programs. See Prepare
Statement:Permitted statements for a list of statements that can be
used.
SIGNAL, RESIGNAL and GET DIAGNOSTICS are exceptions, and may be
used in stored routines
Having said this, even though a SQL statement can be moved into a stored procedure, you may not necessarily want to do that due to code complexity or performance reasons.

BEGIN...END vs START TRANSACTION...COMMIT

What is the difference between doing:
START TRANSACTION
...
COMMIT
Or doing:
BEGIN
...
END
Does the later autocommit, or what might be a practical example of using one of the other?
In both MySQL 5.7 and MySQL 8, BEGIN and END is the same as in T-SQL and represents a "compound statement" also known as "a block of code", just like curly-braces in C, Java, C#, etc.
MySQL 5.7: https://dev.mysql.com/doc/refman/5.7/en/begin-end.html
MySQL 8.0: https://dev.mysql.com/doc/refman/8.0/en/begin-end.html
However, the BEGIN keyword is also (confusingly) overloaded as an alias for BEGIN WORK and START TRANSACTION, and their semantics depend on if they're being used inside a stored program or not:
Within all stored programs (stored procedures and functions, triggers, and events), the parser treats BEGIN [WORK] as the beginning of a BEGIN ... END block. Begin a transaction in this context with START TRANSACTION instead.
So:
START TRANSACTION
Always starts a transaction. You should prefer this syntax.
BEGIN:
If you're in a Stored Procedure, Function, Trigger or Event, then BEGIN by itself marks the start of a compound statement. You can only use START TRANSACTION to start a transaction.
If you're directly executing SQL against MySQL, then this also starts a transaction (as it's interpreted as BEGIN WORK). But it's silly and confusing to use it this way, so avoid it.
BEGIN WORK:
This is an alias for START TRANSACTION. I'd avoid using this completely to prevent confusion.

Committing transactions while executing a postgreql Function

I have Postgresql Function which has to INSERT about 1.5 million data into a table. What I want is I want to see the table getting populated with every one records insertion. Currently what is happening when I am trying with say about 1000 records, the get gets populated only after the complete function gets executed. If I stop the function half way through, no data gets populated. How can I make the record committed even if I stop after certain number of records have been inserted?
This can be done using dblink. I showed an example with one insert being committed you will need to add your while loop logic and commit every loop. You can http://www.postgresql.org/docs/9.3/static/contrib-dblink-connect.html
CREATE OR REPLACE FUNCTION log_the_dancing(ip_dance_entry text)
RETURNS INT AS
$BODY$
DECLARE
BEGIN
PERFORM dblink_connect('dblink_trans','dbname=sandbox port=5433 user=postgres');
PERFORM dblink('dblink_trans','INSERT INTO dance_log(dance_entry) SELECT ' || '''' || ip_dance_entry || '''');
PERFORM dblink('dblink_trans','COMMIT;');
PERFORM dblink_disconnect('dblink_trans');
RETURN 0;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION log_the_dancing(ip_dance_entry text)
OWNER TO postgres;
BEGIN TRANSACTION;
select log_the_dancing('The Flamingo');
select log_the_dancing('Break Dance');
select log_the_dancing('Cha Cha');
ROLLBACK TRANSACTION;
--Show records committed even though we rolled back outer transaction
select *
from dance_log;
What you're asking for is generally called an autonomous transaction.
PostgreSQL does not support autonomous transactions at this time (9.4).
To properly support them it really needs stored procedures, not just the user-defined functions it currently supports. It's also very complicated to implement autonomous tx's in PostgreSQL for a variety of internal reasons related to its session and process model.
For now, use dblink as suggested by Bob.
If you have the flexibility to change from function to procedure, from PostgreSQL 12 onwards you can do internal commits if you use procedures instead of functions, invoked by CALL command. Therefore your function will be changed to a procedure and invoked with CALL command: e.g:
CREATE PROCEDURE transaction_test2()
LANGUAGE plpgsql
AS $$
DECLARE
r RECORD;
BEGIN
FOR r IN SELECT * FROM test2 ORDER BY x LOOP
INSERT INTO test1 (a) VALUES (r.x);
COMMIT;
END LOOP;
END;
$$;
CALL transaction_test2();
More details about transaction management regarding Postgres are available here: https://www.postgresql.org/docs/12/plpgsql-transactions.html
For Postgresql 9.5 or newer you can use dynamic background workers provided by pg_background extension. It creates autonomous transaction. Please, refer the github page of the extension. The sollution is better then db_link. There is a complete guide on Autonomous transaction support in PostgreSQL. There is a third way to start autonomous transaction in Postgres, but some patching neede. Please see Peter's Eisentraut patch proposal for OracleDB-style transactions.

Is there an equivalent for SQL Server's ##error in MySQL

I want to run an update query against a production database and as good little developer I am trying to make it as safe as possible. I am looking to do the following
BEGIN TRANSACTION
UPDATE table_x SET col_y = 'some_value'
.
.
.
IF (##error <> 0)
BEGIN
ROLLBACK
END
ELSE
BEGIN
COMMIT
END
The above should work in SQL Server but I need this to work against a MySQL database.
EDIT:
Sorry, there is more than 1 statement to execute. Yes I am aware of not needing to wrap a single query in a transaction.
BEGIN;
UPDATE foo SET bar = 3;
UPDATE bar SET thing = 5;
COMMIT;
If an error occurs, the entire transaction will be rolled back automatically. You really only need to execute ROLLBACK if something in your application indicates the need to rollback.
It is possible to handle errors explicitly within procedures or compound statements in MySQL, but I wouldn't recommend going down this route. See this how-to article and the docs for DECLARE HANDLER. You'll also have to find the specific error code you want to handle, or you can use the general SQLEXCEPTION condition. You'll also want to review compound statements and defining stored programs.
Anyway, based on the docs, you could do something like the query below, but it honestly wouldn't do anything differently than my previous answer above. It would also get you very strange looks from anyone who uses MySQL (including myself).
BEGIN
DECLARE EXIT HANDLER FOR SQLEXCEPTION ROLLBACK;
START TRANSACTION;
UPDATE foo SET bar = 3;
UPDATE bar SET thing = 5;
COMMIT;
END;
Old answer:
If you're executing a single query, this doesn't make sense. Simply execute the query; if an error occurs, nothing will happen - your transaction will automatically be rolled back.
The reason is that, by default, all single queries are wrapped in a "hidden" transaction, called "autocommit" mode in MySQL. The typical alternative is to explicitly use transactions - once you execute "BEGIN" you have started a transaction. Once you either COMMIT or ROLLBACK you'll be back in autocommit mode.
So, the only reason to use a transaction in MySQL is if you want to rollback to a particular state when an error (or some other external event) occurs. In MySQL, a transaction is always aborted if an error occurs.
Lastly, it is possible to turn this behavior off entirely, and then you must use transactions explicitly at all times. I believe "BEGIN" is implied from when you last committed or rolled back, but you must either COMMIT or ROLLBACK any queries you run.
See The InnoDB Transaction Model in the MySQL manual for more info.
CREATE PROCEDURE prc_test()
BEGIN
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
ROLLBACK;
END;
START TRANSACTION;
INSERT
INTO t_test VALUES ('test', 'test');
INSERT
INTO no_such_table
VALUES ('no');
COMMIT;
END;
CALL prc_test();
SELECT *
FROM t_test;
0 rows fetched.
I don't think this is necessary as there is the concept of implicit commit/rollback.
From MySQL docs:
By default, MySQL starts the session
for each new connection with
autocommit mode enabled, so MySQL does
a commit after each SQL statement if
that statement did not return an
error. If a statement returns an
error, the commit or rollback behavior
depends on the error. See Section
13.6.13, “InnoDB Error Handling”.