MySQL: @variable против переменной. Какая разница?

501

В другом вопросе я написал, кто-то сказал мне, что есть разница между:

@variable

а также:

variable

в MySQL. Он также упомянул, как у MSSQL есть пакетная область, а у MySQL есть область сеанса. Может кто-нибудь уточнить это для меня?

aarona
источник
1
Я знаком с MsSQL, и поэтому мне никогда не приходило в голову задавать такой вопрос. Ответы, представленные здесь, объяснили мне то, о чем я не знал ИДЕИ !! Thx ..
Кен

Ответы:

624

MySQLимеет понятие пользовательских переменных .

Они являются свободно типизированными переменными, которые могут быть инициализированы где-то в сеансе и сохраняют свое значение до завершения сеанса.

Они дополнены @знаком, как это:@var

Вы можете инициализировать эту переменную с помощью SETоператора или внутри запроса:

SET @var = 1

SELECT @var2 := 2

Когда вы разрабатываете хранимую процедуру в MySQL, вы можете передать входные параметры и объявить локальные переменные:

DELIMITER //

CREATE PROCEDURE prc_test (var INT)
BEGIN
    DECLARE  var2 INT;
    SET var2 = 1;
    SELECT  var2;
END;
//

DELIMITER ;

Эти переменные не содержат префиксов.

Разница между переменной процедуры и пользовательской переменной, специфичной для сеанса, заключается в том, что переменная процедуры инициализируется повторно при NULLкаждом вызове процедуры, в то время как переменная, специфичная для сеанса, не имеет значения:

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

Как видите, var2(переменная процедуры) переинициализируется при каждом вызове процедуры, в то время как@var2 ( сеансу) - нет.

(В дополнение к пользовательским переменным, MySQL также имеет некоторые предопределенные «системные переменные», которые могут быть «глобальными переменными», такими как @@global.portили «переменные сеанса», такие как @@session.sql_mode; эти «переменные сеанса» не связаны с пользовательским определением сеанса. переменные.)

Quassnoi
источник
43
Также обратите внимание, что есть глобальные переменные: см. SELECT @@version;Например. Это также причина, почему использование DELIMITER @@не очень хорошая идея.
Mchl
13
это создает новые вопросы для новичков ... есть ли разница между "var = var" и "var: = var", как в вашем примере?
Confiq
13
@confiq: нет ни одного.
Quassnoi
10
Еще один вопрос для новичка. Когда рекомендуется использовать @против нет?
pixelfreak
73
@confiq, @Quassnoi: между :=и есть одно существенное различие, заключающееся в =том, что он :=везде работает как оператор присваивания переменной, а =работает только в SETвыражениях и везде является оператором сравнения. Таким образом SELECT @var = 1 + 1;, @var останется неизменным и вернет логическое значение (1 или 0 в зависимости от текущего значения @var), а SELECT @var := 1 + 1;изменит @var на 2 и вернет 2.
Деви Морган
71

В MySQL @variableуказывает пользовательскую переменную . Вы можете определить свой собственный.

SET @a = 'test';
SELECT @a;

Вне хранимых программ, a variable, без @, является системной переменной , которую вы не можете определить самостоятельно.

Область действия этой переменной - весь сеанс. Это означает, что, хотя ваше соединение с базой данных существует, переменная все еще может использоваться.

Это отличается от MSSQL, где переменная будет доступна только в текущем пакете запросов (хранимая процедура, сценарий или другое). Он не будет доступен в другой партии в том же сеансе.

Molf
источник
2
Не следует путать с переменными сеанса, которые имеют сокращение SET @@a = 'test';, ср. dev.mysql.com/doc/refman/5.1/ru/set-statement.html
RobM
@RobM, они называются системными переменными, а не переменными сеанса.
Pacerier
1
@Pacerier: я неправильно читаю документы? "" "Чтобы явно указать, что переменная является переменной сеанса, перед ее именем укажите SESSION, @@ session. Или @@." ""
RobM
5
@RobM, ты неправильно это читаешь. Прочитайте весь абзац, а не только абзац внутри маркированного списка. Проще говоря, существует два вида переменных сеанса: 1) определяемые пользователем переменные сеанса и 2) определяемые системой  переменные сеанса. Вы не можете установить пользовательскую переменную сеанса с помощью @@. Так , например, set@@my_var=1, set@@session.my_var=1и set session my_var=1не будет работать , потому что my_varэто не система переменной, в то время как мы можем сделать set@@big_tables=1, set@@session.big_tables=1и set session big_tables=1потому , что big_tablesэто системная переменная.
Pacerier
1
@GovindRai: В ответе Quassnoi var2это переменная без @префикса, но это не системная переменная: это переменная процедуры. Это разрешено, потому что он находится в хранимой процедуре (она же хранимая программа) Вне хранимых процедур переменная без @является системной переменной.
LarsH
10

MSSQL требует, чтобы переменные внутри процедур были DECLAREd, и люди используют синтаксис @Variable (DECLARE @TEXT VARCHAR (25) = 'text'). Кроме того, MS допускает объявления в любом блоке процедуры, в отличие от mySQL, который требует всех DECLARE вверху.

Хотя это хорошо для командной строки, я чувствую, что использование «set = @variable» в хранимых процедурах в MySQL рискованно. Там нет области видимости и переменные живут за пределами границ области видимости. Это похоже на объявление переменных в JavaScript без префикса «var», которые затем являются глобальным пространством имен и создают неожиданные коллизии и перезаписи.

Я надеюсь, что хорошие люди в mySQL разрешат DECLARE @Variable на различных уровнях блоков в хранимой процедуре. Обратите внимание на @ (на знак). Префикс @ sign помогает отделить имена переменных от имен столбцов таблицы, поскольку они часто совпадают. Конечно, всегда можно добавить префикс «v» или «l_», но знак @ - это удобный и лаконичный способ, чтобы имя переменной совпадало со столбцом, из которого вы, возможно, извлекаете данные, не забивая их.

MySQL является новым для хранимых процедур, и они сделали хорошую работу для своей первой версии. Будет приятно посмотреть, где они принимают форму, и посмотреть, как развиваются аспекты серверной части языка.

Xybo
источник
3

В принципе, я использую UserDefinedVariables (с префиксом @) в хранимых процедурах. Это облегчает жизнь, особенно когда мне нужны эти переменные в двух или более хранимых процедурах. Просто когда мне нужна переменная только в ОДНОЙ хранимой процедуре, тогда я использую системную переменную (без префикса @).

@Xybo: я не понимаю, почему использование @variables в StoredProcedures должно быть рискованным. Не могли бы вы объяснить, немного «проще» и «границы» (для меня, как новичка)?

Питер
источник
3
Это нарушает основные принципы разработки программного обеспечения. Пожалуйста, не пишите другую строку кода, пока вы точно не знаете, что такое область действия, и почему использование глобальных переменных - вообще ужасная идея. Когда я взял 101 класс программирования, насколько я помню, использование global для почти чего-либо привело бы к автоматическому «F». Есть особые исключения, но, как правило, просто не делайте этого!
BuvinJ
Почему? - @Variables абсолютно обычны в каждой книге MySQL.
Питер
Конечно, в «плоском» сценарии без вызовов функций, процедур, триггеров и т. Д., И если вы просто собираетесь выполнить этот простой сценарий или ограниченный набор команд, а затем завершить сеанс (тем самым уничтожив глобальные переменные). В таком случае, продолжайте и используйте их, если хотите. Но НЕ используйте их внутри функции! Если вы просто используете глобальные переменные или область действия Google, вы мгновенно найдете широкую поддержку идеи, что они повсеместно осуждаются. Вот отправная точка: wiki.c2.com/?GlobalVariablesAreBad или для более общего объяснения: en.wikipedia.org/wiki/Global_variable
BuvinJ
2
В MySQL @variables являются глобальными. Это легко подтверждается. Установите один за пределами функции, а затем оцените его внутри одного. И наоборот, установите один внутри функции и оцените его за ее пределами. Вы увидите, что функция не защищает область таких. Они наступают друг другу на пальцы ног.
BuvinJ
1
Используя терминологию MySQL, @@GLOBALпеременные становятся еще более «глобальными» и коварными. Они пересекают сессии! @variablesимеют «сессионный охват», так что, по крайней мере, они остаются в таком ограничении. Однако на любом нормальном языке это то, что вы называете «глобальной» областью действия (когда они пересекают функции и т. Д.). Концепцию MySQL «глобальный», возможно, следует назвать «универсальной», поскольку она выходит за пределы процесса, выполняющего ее. «Глобальный» обычно не может сделать это на стандартном языке, поскольку процессы не разделяют пространство памяти. Это связано с устойчивой (против изменчивой) тенденцией SQL.
BuvinJ