START TRANSACTION inside BEGIN ... END context or outside and LOOP syntax - mysql

I have two questions about Compound-Statement and Transactions in MySQL.
FIRST:
There are two notes in MySQL Manual:
Note
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.
Note
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.
I can't understand what exactly is meant. They mean that I have to put START TRANSACTION instead of BEGIN or right after BEGIN?
// 1st variant:
BEGIN
START TRANSACTION
COMMIT
END
// 2nd variant:
START TRANSACTION
COMMIT
END
Which one is the right way, 1st variant or 2nd variant?
SECOND:
I don't want to create a Stored Procedure or Function. I just want to create a Compound-Statement Block with a loop inside it in the general flow, like this:
USE 'someDb';
START TRANSACTION
... create table statement
... insert statement
// now I want to implement some insert/select statements using loop, I do as follows:
DELIMITER $
BEGIN
SET #n = 1, #m = 2;
lab1: LOOP
... some insert, select statements here
END LOOP lab1;
END $
DELIMITER ;
END
COMMIT
Is it possible such kind of structure? Because I have an error thrown:
Query: BEGIN SET #n = 1, #m = 2; lab1: LOOP SELECT ...
Error Code: 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 #n = 1, #m = 2;
lab1: LOOP SELECT ...
My questions are:
Is it allowed to use BEGIN...END just in general flow without creating and using Stored Procedures or Functions?
Is it allowed to use BEGIN...END inside of START TRANSACTION...COMMIT or I have to put START TRANSACTION...COMMIT inside of BEGIN...END?
BEGIN
START TRANSACTION
COMMIT
END
// vs.
START TRANSACTION
BEGIN
END
COMMIT
Do I by all means have to use BEGIN...END if I want to use only LOOP? May I just use LOOP syntax without starting BEGIN...END? The only example in the manual for LOOP is this:
CREATE PROCEDURE doiterate(p1 INT)
BEGIN
label1: LOOP
...

Is it allowed to use BEGIN...END just in general flow without creating and using Stored Procedures or Functions?
No: compound statements can only be used within the body of stored programs.
Is it allowed to use BEGIN...END inside of START TRANSACTION...COMMIT or I have to put START TRANSACTION...COMMIT inside of BEGIN...END?
START TRANSACTION; and COMMIT; are separate statements. If you want the body of a stored program to contain multiple statements, it will need to enclose those statements in some sort of compound statement block such as BEGIN ... END (which is similar to enclosing a block of statements in braces { ... } within a C-like language).
That said, you could have a stored program which contains only the single-statement START TRANSACTION; or COMMIT;—such a program would not require any compound statement block and would merely commence a new / commit the current transaction respectively.
Outside of a stored program, where compound statement blocks are not permitted, you can issue START TRANSACTION; and COMMIT; statements as & when required.
Do I by all means have to use BEGIN...END if I want to use only LOOP? May I just use LOOP syntax without starting BEGIN...END?
LOOP is also a compound statement block, which is only valid within a stored procedure. It is not necessary to enclose a LOOP block within a BEGIN ... END block, although it is usual (as otherwise it is difficult to perform any required loop initialisation).
In your case, where you apparently want to insert data into a table from a looping construct, you will either need to:
define a stored program in which you use LOOP;
iterate a loop in an external program that executes database queries on each iteration; or
redefine your logic in terms of sets upon which SQL can directly operate.

Related

MySQL Cursors - having errors in MySQL Workbench

I am trying to write a mysql program, which has cursors in it. Due to an error in writing an DECLARE query, MySQL Workbench is always showing me the DECLARE is not valid at this position, expected EOF, ALTER, ANALYZE, BEGIN, BINLOG, CACHE, ...
Could you help me solve this problem?
Here is my code:
DELIMITER //
BEGIN
declare Naslov_knjige VARCHAR(24);
declare Cena_knjige DECIMAL(8,2);
DECLARE cursor_cene CURSOR
FOR SELECT
Naslov,
Cena
FROM
prvi_test_v2.knjige;
OPEN cursor_cene //
FETCH NEXT FROM cursor_cene INTO
#Naslov_knjige,
#Cena_knjige //
WHILE ##FETCH_STATUS = 0
BEGIN
PRINT #Naslov_knjige + CAST(#Cena_knjige AS VARCHAR) //
FETCH NEXT FROM cursor_cene INTO
#Naslov_knjige,
#Cena_knjige //
END //
CLOSE cursor_cene //
DEALLOCATE cursor_cene //
END //
DELIMITER ;
Thanks for your help!
I will assume you omitted a line for CREATE PROCEDURE, because in MySQL a BEGIN...END block must be part of a stored routine. See https://dev.mysql.com/doc/refman/8.0/en/begin-end.html
BEGIN ... END syntax is used for writing compound statements, which can appear within stored programs (stored procedures and functions, triggers, and events).
You changed the DELIMITER:
DELIMITER //
Using this delimiter terminates the whole CREATE PROCEDURE statement. You should not do this after the first statement in the body of the procedure. You need to use the normal ; terminators for each statement within the body of the procedure. The reason for changing the delimiter is so you can use ; for each statement in the procedure without ending the CREATE PROCEDURE.
See examples and documentation here: https://dev.mysql.com/doc/refman/8.0/en/stored-programs-defining.html
That's the reason for the error you got. You used // to terminate OPEN cursor_cene // which ended the CREATE PROCEDURE, but clearly there was more to that procedure.
There are other problems with your procedure. You seem to be using Microsoft SQL Server syntax, but MySQL is different.
Naslov_knjige is not the same variable as #Naslov_knjige in MySQL. Don't use the # sigil in front of local variables. If you use the # sigil, this refers to a user-defined variable.
The WHILE ##FETCH_STATUS = 0 syntax is specific to Microsoft SQL Server. MySQL has different syntax for running a cursor loop. See example in the documentation: https://dev.mysql.com/doc/refman/8.0/en/cursors.html
That's as far as I got. There may be more problems, but I am not going to look for them.

Add MySql stored procedure via Propel [duplicate]

In my model I defined some procedures. The code (generated by MySQL Workbench) contains DELIMITER definitions, so the procedures look like:
-- schema
CREATE DATABASE ...
CREATE TABLE foo ...
-- procedures
DELIMITER $$
...
BEGIN
DECLARE ... ;
OPEN ... ;
SET ... ;
... ;
END$$
DELIMITER ;
Now I need to "import" the SQL to the database via PDO. I tried to pass it as input for the PDO#exec(...), but noticed, that the execution stops on the line of the first DELIMITER definition.
I don't want remove the DELIMITER statements. So the the SQL code should remain the same.
How to use PDO to execute SQL code containing DELIMITER statements?
From comments:
I don't want remove the DELIMITER statements. And actually I want to get it working without to execute every statement manually
That's not how it works.
To understand why, you need to understand how the mysql CLI -- and any other program that can read and execute a dump file like this -- actually handles it.
DELIMITER is not something the server understands.
DELIMITER is used to tell the client-side parser what the current statement delimiter should be, so that the client-side parser can correctly split the statements and deliver one at a time to the server for execution.
From the docs. Note carefully that mysql, every time it is used here, refers to the mysql client utility -- not the server.
If you use the mysql client program to define a stored program containing semicolon characters, a problem arises. By default, mysql itself recognizes the semicolon as a statement delimiter, so you must redefine the delimiter temporarily to cause mysql to pass the entire stored program definition to the server.
To redefine the mysql delimiter, use the delimiter command. [...] The delimiter is changed to // to enable the entire definition to be passed to the server as a single statement, and then restored to ; before invoking the procedure. This enables the ; delimiter used in the procedure body to be passed through to the server rather than being interpreted by mysql itself.
https://dev.mysql.com/doc/refman/5.7/en/stored-programs-defining.html
So, to handle such a file, you need a client-side parser that does the same thing mysql does... and here, the code you are writing is (needs to be) the client-side statement parser. So you are the one that needs to write the logic to handle the delimiter.
To do what you want, you have to interpret the DELIMITER statements, use them to keep track of the current statement delimiter, but do not send them to the server.
Then, you have to read through the input one line at a time, buffering what you've read, until you find the specified delimiter at the end of the line, and send the resulting statement to the server -- excluding the actual statement delimiter from what you send... so, for example, you would not send the ending $$ after the procedure body (unless the current statement delimiter is ;, which you can either send or not send -- the server doesn't care.) Then empty the buffer and start reading again until you see another instance of a delimiter (and send the statement to the server) or match a DELIMITER statement and set your code's current delimiter variable to match it so that you correctly identify the end of the next statement.
Delimiters is a thing that you don't need with PDO. You can just run your queries as is
$pdo->query("CREATE DATABASE ...");
$pdo->query("CREATE TABLE foo ...");
$pdo->query("BEGIN
DECLARE ... ;
OPEN ... ;
SET ... ;
... ;
END");
as simple as that
I met same problem with you when I tried with PostgreSQL. The problem seems PDO just allow you execute 1 query 1 time. As mentioned: PDO::exec() executes an SQL statement in a single function call, returning the number of rows affected by the statement. In php manual
Could you try this:
$stmt = $db->prepare($sql);
$stmt->execute();
Or with mysqli: multi_query. php manual
Here my whole class: http://sandbox.onlinephpfunctions.com/code/f0528fda6d7bd097c3199f1f3c019805a163ae3a

Error 1064 declaring variable inside trigger

I'm having trouble declaring a variable inside a trigger.
SET DELIMITER ;;
BEGIN
DECLARE qtyNow INT;
SET qtyNow = (
SELECT qty
FROM warehouse
WHERE bin_id = 'GA66'
);
DECLARE need INT;
SET need = (
SELECT min_level
FROM warehouse
WHERE bin_id = 'GA66'
);
END;;
SET DELIMITER ;
I get error #1064 which means illegal syntax. I don't see where I went wrong. I even removed all that bulk and just had
DECLARE qtyNow INT;
And this single line still pops the error.
I see two problems.
One, you have a BEGIN...END block but you are not declaring a trigger.
https://dev.mysql.com/doc/refman/5.7/en/begin-end.html says:
BEGIN ... END syntax is used for writing compound statements, which can appear within stored programs (stored procedures and functions, triggers, and events).
You can't use BEGIN...END as a bare statement. It must be part of a CREATE TRIGGER, CREATE PROCEDURE, CREATE FUNCTION, or CREATE EVENT.
Two, you have two DECLARE statements in your block, with a SET in between.
https://dev.mysql.com/doc/refman/5.7/en/declare.html says:
DECLARE is permitted only inside a BEGIN ... END compound statement and must be at its start, before any other statements.
You are trying to use a second DECLARE after you have done another statement, SET. Do all your DECLAREs up front.

Call a stored procedure from the DECLARE statement when using cursors in MySQL

I am trying to use a cursor in MySQL to call a stored procedure many times. I want to call it as many times as a value for my_id exists in some temporary table, and iterate through those ids and concatenate the results.
Anyway, I'm having trouble with this part of the process:
DECLARE curs CURSOR FOR
SELECT something FROM somewhere;
I don't want to select something from somewhere. I want something like
DECLARE curs CURSOR FOR
CALL storedproc(#an_id);
Can the DECLARE statement be used to call a stored procedure? Or does it have to be associated with a SELECT only? Googling around, I'm afraid that the latter is the case.
Using a cursor requires some standard boilerplate code to surround it.
Using a cursor to call a stored procedure for each set of values from the table requires essentially the same boilerplate. You SELECT the values you want to pass, from wherever you're getting them (which could be a temporary table, base table, or view, and can include calls to stored functions) and then call the procedure with those values.
I've written an syntactically valid example of that boilerplate code, below, with comments to explain what each component is doing. There are few things I dislike more than being asked to just do something "just because" -- so everything is (hopefully) explained.
You mentioned calling the procedure with multiple values, so this example uses 2.
Note that there events that happen her are in a specific order for a reason. Variables have to be declared first, cursors have to be declared before their continue handlers, and loops have to follow all of those things. This gives an impression that there's some fairly extreme inflexibility, here, but that's not really the case. You can reset the ordering by nesting additional code inside BEGIN ... END blocks within the procedure body; for example, if you needed a second cursor inside the loop, you'd just declare it inside the loop, inside another BEGIN ... END.
DELIMITER $$
DROP PROCEDURE IF EXISTS `my_proc` $$
CREATE PROCEDURE `my_proc`(arg1 INT) -- 1 input argument; you might not need one
BEGIN
-- from http://stackoverflow.com/questions/35858541/call-a-stored-procedure-from-the-declare-statement-when-using-cursors-in-mysql
-- declare the program variables where we'll hold the values we're sending into the procedure;
-- declare as many of them as there are input arguments to the second procedure,
-- with appropriate data types.
DECLARE val1 INT DEFAULT NULL;
DECLARE val2 INT DEFAULT NULL;
-- we need a boolean variable to tell us when the cursor is out of data
DECLARE done TINYINT DEFAULT FALSE;
-- declare a cursor to select the desired columns from the desired source table1
-- the input argument (which you might or might not need) is used in this example for row selection
DECLARE cursor1 -- cursor1 is an arbitrary label, an identifier for the cursor
CURSOR FOR
SELECT t1.c1,
t1.c2
FROM table1 t1
WHERE c3 = arg1;
-- this fancy spacing is of course not required; all of this could go on the same line.
-- a cursor that runs out of data throws an exception; we need to catch this.
-- when the NOT FOUND condition fires, "done" -- which defaults to FALSE -- will be set to true,
-- and since this is a CONTINUE handler, execution continues with the next statement.
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
-- open the cursor
OPEN cursor1;
my_loop: -- loops have to have an arbitrary label; it's used to leave the loop
LOOP
-- read the values from the next row that is available in the cursor
FETCH NEXT FROM cursor1 INTO val1, val2;
IF done THEN -- this will be true when we are out of rows to read, so we go to the statement after END LOOP.
LEAVE my_loop;
ELSE -- val1 and val2 will be the next values from c1 and c2 in table t1,
-- so now we call the procedure with them for this "row"
CALL the_other_procedure(val1,val2);
-- maybe do more stuff here
END IF;
END LOOP;
-- execution continues here when LEAVE my_loop is encountered;
-- you might have more things you want to do here
END $$
DELIMITER ;
Can the DECLARE statement be used to call a stored proc?
Not possible and documentation is pretty clear on that
Cursor DECLARE Syntax
This statement declares a cursor and associates it with a SELECT statement that retrieves the rows to be traversed by the cursor. To fetch the rows later, use a FETCH statement. The number of columns retrieved by the SELECT statement must match the number of output variables specified in the FETCH statement.

MYSQL If statement in transaction causing error

I have the following MYSQL query:
START TRANSACTION;
SELECT sport_id INTO #a FROM sports WHERE sport_id = 2 FOR UPDATE;
UPDATE sports SET sport_name = 'Table Tennis' WHERE sport_id = #a;
if (#a > 1) then
COMMIT;
ELSE
ROLLBACK;
END IF;
The problem is that it returns an error at the if statement:
#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 'if (#a > 1) then COMMIT' at line 1
I've looked on stack overflow and there is an answer showing a similar query, written in pretty much the same way, but they are using the variable without an # symbol. Removing the # for my query does not resolve the issue.
This is just a test query to try out some transactions using MYSQL, hence why the query seems a little pointless. I'm a little stuck.
MySQL doesn't recognize a statement beginning with the keyword IF as as a valid SQL statement.
The IF statement works only in the context of a compound statement (that is, a block of statements enclosed between BEGIN and END. Currently, the compound statement is only supported in the context of a stored program (stored procedure, function or trigger.)
http://dev.mysql.com/doc/refman/5.5/en/begin-end.html
For testing, try...
DELIMITER //
CREATE PROCEDURE usp_test_transaction()
BEGIN
DECLARE EXIT HANDLER FOR SQLEXCEPTION ROLLBACK;
START TRANSACTION;
-- whatever DML operations and SELECT statements you want to perform go here
IF (1=1) THEN
COMMIT;
ELSE
ROLLBACK;
END IF;
END//
DELIMITER ;
CALL usp_test_transaction;
(NOTE: I am not advocating here that transactions be handled inside of stored procedure. My personal preference is to NOT do this, and instead handle transactions at a higher level. But the example above should work; I believe MySQL does support calling START TRANSACTION, COMMIT and ROLLBACK within the context of a stored procedure.)