In another question I posted someone told me that there is a difference between:
#variable
and:
variable
in MySQL. He also mentioned how MSSQL has batch scope and MySQL has session scope. Can someone elaborate on this for me?
MySQL has a concept of user-defined variables.
They are loosely typed variables that may be initialized somewhere in a session and keep their value until the session ends.
They are prepended with an # sign, like this: #var
You can initialize this variable with a SET statement or inside a query:
SET #var = 1
SELECT #var2 := 2
When you develop a stored procedure in MySQL, you can pass the input parameters and declare the local variables:
DELIMITER //
CREATE PROCEDURE prc_test (var INT)
BEGIN
DECLARE var2 INT;
SET var2 = 1;
SELECT var2;
END;
//
DELIMITER ;
These variables are not prepended with any prefixes.
The difference between a procedure variable and a session-specific user-defined variable is that a procedure variable is reinitialized to NULL each time the procedure is called, while the session-specific variable is not:
CREATE PROCEDURE prc_test ()
BEGIN
DECLARE var2 INT DEFAULT 1;
SET var2 = var2 + 1;
SET #var2 = #var2 + 1;
SELECT var2, #var2;
END;
SET #var2 = 1;
CALL prc_test();
var2 #var2
--- ---
2 2
CALL prc_test();
var2 #var2
--- ---
2 3
CALL prc_test();
var2 #var2
--- ---
2 4
As you can see, var2 (procedure variable) is reinitialized each time the procedure is called, while #var2 (session-specific variable) is not.
(In addition to user-defined variables, MySQL also has some predefined "system variables", which may be "global variables" such as ##global.port or "session variables" such as ##session.sql_mode; these "session variables" are unrelated to session-specific user-defined variables.)
In MySQL, #variable indicates a user-defined variable. You can define your own.
SET #a = 'test';
SELECT #a;
Outside of stored programs, a variable, without #, is a system variable, which you cannot define yourself.
The scope of this variable is the entire session. That means that while your connection with the database exists, the variable can still be used.
This is in contrast with MSSQL, where the variable will only be available in the current batch of queries (stored procedure, script, or otherwise). It will not be available in a different batch in the same session.
MSSQL requires that variables within procedures be DECLAREd and folks use the #Variable syntax (DECLARE #TEXT VARCHAR(25) = 'text'). Also, MS allows for declares within any block in the procedure, unlike MySQL which requires all the DECLAREs at the top.
While good on the command line, I feel using the set = #variable within stored procedures in MySQL is risky. There is no scope and variables live across scope boundaries. This is similar to variables in JavaScript being declared without the var prefix, which are then the global namespace and create unexpected collisions and overwrites.
I am hoping that the good folks at MySQL will allow DECLARE #Variable at various block levels within a stored procedure. Notice the # (at sign). The # sign prefix helps to separate variable names from table column names - as they are often the same. Of course, one can always add an "v" or "l_" prefix, but the # sign is a handy and succinct way to have the variable name match the column you might be extracting the data from without clobbering it.
MySQL is new to stored procedures and they have done a good job for their first version. It will be a pleasure to see where they take it form here and to watch the server side aspects of the language mature.
In principle, I use UserDefinedVariables (prepended with #) within Stored Procedures. This makes life easier, especially when I need these variables in two or more Stored Procedures. Just when I need a variable only within ONE Stored Procedure, than I use a System Variable (without prepended #).
#Xybo:
I don't understand why using #variables in StoredProcedures should be risky. Could you please explain "scope" and "boundaries" a little bit easier (for me as a newbe)?
#variable is very useful if calling stored procedures from an application written in Java , Python etc.
There are ocassions where variable values are created in the first call and needed in functions of subsequent calls.
Side-note on PL/SQL (Oracle)
The advantage can be seen in Oracle PL/SQL where these variables have 3 different scopes:
Function variable for which the scope ends when function exits.
Package body variables defined at the top of package and outside all functions whose scope is the session and visibility is package.
Package variable whose variable is session and visibility is global.
My Experience in PL/SQL
I have developed an architecture in which the complete code is written in PL/SQL. These are called from a middle-ware written in Java. There are two types of middle-ware. One to cater calls from a client which is also written in Java. The other other one to cater for calls from a browser.
The client facility is implemented 100 percent in JavaScript. A command set is used instead of HTML and JavaScript for writing application in PL/SQL.
I have been looking for the same facility to port the codes written in PL/SQL to another database. The nearest one I have found is Postgres. But all the variables have function scope.
Opinion towards # in MySQL
I am happy to see that at least this # facility is there in MySQL. I don't think Oracle will build same facility available in PL/SQL to MySQL stored procedures since it may affect the sales of Oracle database.
Related
This question already has answers here:
MySQL: #variable vs. variable. What's the difference?
(5 answers)
Closed 2 years ago.
I am trying to learn MySQL and after doing some database, I am having the following questions.
When declaring a variable inside a procedure or in the function I can do it like this:
using # sign, like this: SET #varname = varcontent
using DECLARE keyword, like this: DECLARE varname vartype
using directly SET without DECLARE, like this SET varname = varcontent
My doubts are:
what type of syntax is correct, using DECLARE, # or SET?
what would be the correct form of doing it?
Also, I would like if you could recommend some good practices with MySQL.
varname and #varname are two DIFFERENT variables!
#varname - user-defined variable.
Exists always. Even when it was not initialized and/or used, in such case it has NULL value. I.e. it does not need in declaration.
Have no datatype (or it has dynamic datatype). Datatype may be easily changed by assigning the value of another datatype.
This variable has a scope of a connection. I.e. it exists until the connection exists, and its value is not altered until it is altered explicitly. Each connection has its own variavle with the same name, they do not interfere.
For example, you may set it to some value, then use/alter this value in called stored procedure, then use the value altered in the procedure in outer code after the procedure finished.
varname - local (declared) variable
Not exists in anonymous code, exists only within compound (BEGIN-END) code block. Must be declared explicitly at the beginning of the block. Is destroyed at the end of the block. Special type of local variable is function/procedure parameter - it is declared in function/procedure header and exists within the function/procedure code block.
Has definite datatype. Cannot be re-declared.
Has a scope of a block where it is defined.
If a variable and a column with the same name exists in some scope, then the variable has priority and masks the column, so if you need to access the column you must specify table alias.
In most constructions any variable type may be used. But sometimes only one of types may be used - consult User Manual.
1.using # like set #varname= varcontent
This statement initializes user-defined variable with some value.
2.using declare like declare varname vartype
This statement declares local variable. May posess at the beginning of BEGIN-END block. Does not set a value to the variable (it is NULL).
3.using directly set varname=varcontent without declare
Causes an error.
I am trying to create a stored procedure in MySQL which is not supposed to be vulnerable to SQL injection. Hence I am using prepared statements inside this. I have a Patient table to which I want to add data using this procedure. This is what my stored procedure looks like.
DROP PROCEDURE IF EXISTS CreatePatient;
DELIMITER ##
CREATE PROCEDURE CreatePatient (IN alias VARCHAR(20))
BEGIN
PREPARE q1 FROM 'insert into Patient values (?)';
set #alias = alias;
EXECUTE q1 USING #alias;
END ##
DELIMITER ;
When I tried to run this without setting a new variable #alias,
EXECUTE q1 USING alias;
I am getting an SQL syntax error. From my understanding, it doesn't seem right to create a variable within the method body just to assign it the input variable to the procedure. What am I missing here?
Mysql has 3 types of variables
User Defined Variables
Local variables
session variables
User defined variables have session scope while local variables have a block scope i.e within BEGIN-END Block.
Because local variables are in scope only during stored program execution, references to them are not permitted in prepared statements created within a stored program. Prepared statement scope is the current session, not the stored program, so the statement could be executed after the program ends, at which point the variables would no longer be in scope. For example, SELECT ... INTO local_var cannot be used as a prepared statement. This restriction also applies to stored procedure and function parameters
See official docs
Here's the start of my typical stored procedure:
CREATE DEFINER=`joe`#`%` PROCEDURE `Add_Item`(
IN usernameApp VARCHAR(255),
IN barcodeApp VARCHAR(255),
IN quantityApp VARCHAR(255)
)
BEGIN
I would call it with something like this code from PHP:
CALL Add_Item('ethan', '987261826671', '12');
The issue is that I am looking for something a bit more dynamic, where I can call the stored procedure with parameters in any order (because I can't guarantee the order in my dynamic app I'm trying to create). I feel like named parameters would work, but I know MySQL doesn't have that for procedures.
Something like this would work, for example (pseudo code obviously):
CALL Add_Item(quantity>'12' name>'ethan', barcode>'987261826671');
Ideas?
Using PDO:
$sth = $dbh->prepare('CALL Add_Item(:quantity, :name, :barcode)');
// You can pass paremeters in any order here:
$sth->execute([
':quantity' => 12,
':name' => 'ethan',
':barcode' => '987261826671',
]);
The arguments to a stored proc are fixed. There are no optional arguments and the order is fixed. There's no such thing as named arguments like in Perl or Python.
A workaround you could do is the following:
SET #quantity = 12;
SET #name = 'ethan';
SET #barcode = '987261826671';
CALL Add_Item();
In other words, use session variables instead of procedure arguments. Then you can set the session variables in any order you want.
Session variables can be referenced within procedure code simply by using the # prefix. Session variables are visible only within the current session.
But this workaround doesn't work well for recursive procedures, where you want to pass parameters to the procedure in recursive calls.
Also, if you call the procedure multiple times during your session, you'd have to remember to change the values or else the values from prior calls could carry over to subsequent calls.
I have to say this is an odd question. Many programming languages, even PHP, require you to call a function or procedure with arguments in a specific order. This isn't too difficult a constraint.
I tried finding an answer to this online, but could not find any clear explanation:
Does the # in a stored procedure serve some sort of special purpose/signify something in particular? I am a little confused as to when we use it, since examples seem to vary on its usage.
For instance in the following example # is used:
DELIMITER $
DROP PROCEDURE IF EXISTS emp_count_2;
CREATE PROCEDURE emp_count_2(OUT param1 INT)
BEGIN
SELECT COUNT(*) INTO param1 FROM Employee;
END
$
DELIMITER ;
/* To invoke this procedure use the mysql command statement
CALL emp_count_2(#empCount);
SELECT #empCount;
*/
Once again, does the # in this example serve some sort of special purpose, or can we remove the # and just use normal variable names?
**EDIT: I am using MySql
The #variable syntax in MySQL denotes a user-defined session variable. You can set these user variables outside a stored procedure, but you can also set them inside a stored procedure, and the effect is that the variable retains the value after your procedure call returns.
So in your example, the following would also do the same thing:
CREATE PROCEDURE emp_count_2()
BEGIN
SELECT COUNT(*) INTO #empCount FROM Employee;
END
CALL emp_count_2(); /* sets #empCount as a side-effect */
SELECT #empCount;
It's okay for multiple sessions to set the user variable in this way concurrently, because user variables are scoped to a single session, and concurrent sessions may have variables of the same name, but with different values.
The variable syntax with no # prefix is for variables local to the procedure, either procedure parameters, or else local variables declared with DECLARE within the procedure body.
This usage you have, passing a user variable as a parameter and assigning it in the body of the procedure, is useful if you want to call a procedure several times and store the result in separate user variables. Otherwise each call to the procedure would overwrite the previous value in the #empCount user variable for the current session.
I have a recursive mysql stored procedures for which I have set the max_sp_recursion_depth=10.
Now, without setting a local variable, i would like to know what recursion's level is during single execution.
I think that surely there is a session variable that stores the depth (how else would you know when you reach the maximum level) but I could not find it. I would avoid using a variable to do this incrementally. How could i know this (if any) system variable?
I know you specifically asked how to do this without a user-created variable - but for others coming across this thought it would be worth posting how to do it with one as it's fairly simple:
CREATE PROCEDURE sp_recursive
BEGIN
// ... DECLAREs here
-- Set maximum recursion depth (max is 255)
SET ##SESSION.max_sp_recursion_depth = 10;
-- Increment current recursion depth
SET #recursion_depth = IFNULL(#recursion_depth + 1, 1);
-- ... More stored procedure code
-- Decrement current recursion depth. Note: Care must be taken to ensure this line
-- is *always* executed at the end of the stored procedure.
SET #recursion_depth = #recursion_depth - 1;
END
Explanation
The #recursion_depth session-scoped variable is incremented by the above SET statement each time the stored procedure is entered. The first time it is entered, the variable is uninitialized and so has a value of NULL - this is checked for by the IFNULL(), which reassigns it to one in this case. At the end of the stored procedure just before exiting, the depth needs to be decremented.
Further notes
Worth noting that SQL Server does provide an in-built ##NESTLEVEL variable for doing the above - but unfortunately MySQL doesn't seem to have an equivalent.