MySQL: @ zmienna vs. zmienna. Co za różnica?

500

W innym pytaniu, które napisałem, ktoś powiedział mi, że istnieje różnica między:

@variable

i:

variable

w MySQL. Wspomniał także o tym, jak MSSQL ma zakres wsadowy, a MySQL ma zakres sesji. Czy ktoś może mi to rozwinąć?

aarona
źródło
1
Znam MsSQL, więc nigdy nie przyszło mi do głowy, aby zadać takie pytanie. Odpowiedzi podane tutaj wprowadziły mnie w coś, o czym nie miałem pomysłu !! Dzięki ..
Ken

Odpowiedzi:

623

MySQLma pojęcie zmiennych zdefiniowanych przez użytkownika .

Są to zmienne o luźnym typie, które mogą być inicjowane gdzieś w sesji i zachowują swoją wartość aż do końca sesji.

Są one poprzedzone @znakiem:@var

Możesz zainicjalizować tę zmienną za pomocą SETinstrukcji lub wewnątrz zapytania:

SET @var = 1

SELECT @var2 := 2

Po opracowaniu procedury składowanej w MySQLmożna przekazać parametry wejściowe i zadeklarować zmienne lokalne:

DELIMITER //

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

DELIMITER ;

Te zmienne nie są poprzedzone żadnymi prefiksami.

Różnica między zmienną procedury a zmienną zdefiniowaną przez użytkownika specyficzną dla sesji polega na tym, że zmienna procedury jest inicjowana ponownie za NULLkażdym razem, gdy wywoływana jest procedura, podczas gdy zmienna specyficzna dla sesji nie jest:

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

Jak widać, var2(zmienna procedury) jest ponownie inicjowana za każdym razem, gdy wywoływana jest procedura, podczas gdy @var2(zmienna specyficzna dla sesji) nie jest.

(Oprócz zmiennych zdefiniowanych przez użytkownika, MySQL ma również pewne predefiniowane „zmienne systemowe”, które mogą być „zmiennymi globalnymi”, takimi jak @@global.portlub „zmiennymi sesji”, takimi jak @@session.sql_mode; te „zmienne sesji” nie są powiązane z konkretnymi sesjami zdefiniowanymi przez użytkownika zmienne).

Quassnoi
źródło
43
Pamiętaj również, że dostępne są zmienne globalne: patrz SELECT @@version;na przykład. Jest to również powód, dla którego używanie DELIMITER @@nie jest tak naprawdę dobrym pomysłem.
Mchl
13
tworzy nowe pytania o nowe wyniki ... czy jest jakaś różnica między „var = var” a „var: = var” jak w twoim przykładzie?
confiq
13
@confiq: nie ma.
Quassnoi
9
Kolejne pytanie dla nowicjusza. Kiedy zaleca się stosowanie @vs nie?
pixelfreak
73
@confiq, @Quassnoi: istnieje jedna znacząca różnica między :=i =, i to znaczy, że :=działa wszędzie jako operator przypisywania zmiennych, podczas gdy =działa tylko w SETinstrukcjach i jest operatorem porównania wszędzie indziej. Więc SELECT @var = 1 + 1;pozostawi @var bez zmian i zwróci wartość logiczną (1 lub 0 w zależności od bieżącej wartości @var), podczas gdy SELECT @var := 1 + 1;zmieni @var na 2 i zwróci 2.
Dewi Morgan
71

W MySQL @variablewskazuje zmienną zdefiniowaną przez użytkownika . Możesz zdefiniować własne.

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

Poza przechowywanymi programami, variablebez @, jest zmienną systemową , której nie możesz sam zdefiniować.

Zakres tej zmiennej obejmuje całą sesję. Oznacza to, że dopóki istnieje połączenie z bazą danych, zmiennej można nadal używać.

Jest to w przeciwieństwie do MSSQL, w którym zmienna będzie dostępna tylko w bieżącej partii zapytań (procedura składowana, skrypt lub w inny sposób). Nie będzie dostępny w innej partii w tej samej sesji.

Molf
źródło
2
Nie mylić ze zmiennymi sesji, które mają skrót SET @@a = 'test';, por. dev.mysql.com/doc/refman/5.1/en/set-statement.html
RobM
@RobM, nazywane są zmiennymi systemowymi , a nie zmiennymi sesyjnymi.
Pacerier
1
@Pacerier: Czy źle czytam dokumenty? „” „Aby wyraźnie wskazać, że zmienna jest zmienną sesyjną, poprzedź jej nazwę SESSION, @@ session. Lub @@.” ””
RobM
5
@RobM, czytasz to źle. Przeczytaj cały akapit, nie tylko akapit w punkcie wypunktowanym. Mówiąc najprościej, istnieją dwa rodzaje zmiennych sesji: 1) Zmienne sesji zdefiniowane przez użytkownika i 2)  Zmienne sesji zdefiniowane przez system . Za pomocą nie można ustawić zmiennej sesji zdefiniowanej przez użytkownika @@. Na przykład set@@my_var=1, set@@session.my_var=1i set session my_var=1nie działa, ponieważ my_varnie jest to układ zmienna, podczas gdy możemy zrobić set@@big_tables=1, set@@session.big_tables=1i set session big_tables=1dlatego big_tablesjest zmienna systemowa.
Pacerier
1
@GovindRai: W odpowiedzi Quassnoi var2jest zmienną bez @przedrostka, ale nie jest zmienną systemową: jest zmienną proceduralną. Jest to dozwolone, ponieważ jest to procedura składowana (inaczej program przechowywany). Poza procedurami składowanymi zmienna bez @jest zmienną systemową.
LarsH
10

MSSQL wymaga, aby zmienne w ramach procedur były ZADEKLAROWANE, a ludzie używali składni @Variable (DECLARE @TEXT VARCHAR (25) = 'text'). Ponadto MS pozwala na deklarowanie w dowolnym bloku procedury, w przeciwieństwie do mySQL, który wymaga wszystkich DEKLARACJI na górze.

Chociaż dobrze w wierszu poleceń, uważam, że użycie „set = @variable” w ramach procedur przechowywanych w mySQL jest ryzykowne. Nie ma zasięgu, a zmienne działają ponad granicami zakresu. Jest to podobne do deklarowanych zmiennych w JavaScript bez prefiksu „var”, które są wówczas globalną przestrzenią nazw i tworzą nieoczekiwane kolizje i nadpisania.

Mam nadzieję, że dobrzy ludzie na mySQL pozwolą DECLARE @ Zmienna na różnych poziomach bloków w ramach procedury przechowywanej. Zwróć uwagę na @ (przy znaku). Prefiks @ pomaga oddzielić nazwy zmiennych od nazw kolumn tabeli - ponieważ często są one takie same. Oczywiście zawsze można dodać przedrostek „v” lub „l_”, ale znak @ jest przydatnym i zwięzłym sposobem, aby nazwa zmiennej pasowała do kolumny, z której można wyodrębnić dane bez blokowania jej.

MySQL jest nowy w procedurach przechowywanych i wykonał dobrą robotę dla swojej pierwszej wersji. Przyjemnością będzie zobaczenie, jak się tutaj przyjmują i obserwowanie dojrzałych aspektów serwera po stronie serwera.

Xybo
źródło
3

Zasadniczo używam UserDefinedVariables (poprzedzone @) w ramach procedur przechowywanych. Ułatwia to życie, szczególnie gdy potrzebuję tych zmiennych w dwóch lub więcej procedur przechowywanych. Właśnie wtedy, gdy potrzebuję zmiennej tylko w JEDNEJ Procedurze Przechowywanej, używam Zmiennej Systemowej (bez poprzedzającego @).

@Xybo: Nie rozumiem, dlaczego używanie @variables w StoredProcedures powinno być ryzykowne. Czy mógłbyś wyjaśnić nieco „zakres” i „granice” (dla mnie jako nowicjusza)?

Piotr
źródło
3
Narusza to podstawowe zasady inżynierii oprogramowania. Nie pisz innego wiersza kodu, dopóki nie dowiesz się dokładnie, jaki jest zakres, i dlaczego używanie zmiennych globalnych jest ogólnie okropnym pomysłem. Kiedy wziąłem 101 lekcji programowania, jak pamiętam, użycie globalnego dla prawie wszystkiego skutkowałoby automatycznym „F”. Istnieją specjalne wyjątki, ale z reguły - po prostu nie rób tego!
BuvinJ
Dlaczego? - @ Zmienne są absolutnie powszechne w każdej książce MySQL.
Peter
Jasne, w „płaskim” skrypcie bez wywołań funkcji, procedur, wyzwalaczy itp., A jeśli zamierzasz po prostu wykonać ten prosty skrypt lub ograniczony zestaw poleceń, a następnie zakończyć sesję (niszcząc w ten sposób globały). W takim przypadku skorzystaj z nich, jeśli chcesz. Ale NIE używaj ich wewnątrz funkcji! Jeśli po prostu zmienne globalne lub zakres Google są od razu dostępne, natychmiast znajdziesz szerokie poparcie dla idei, że są one powszechnie odrzucane. Oto punkt wyjścia: wiki.c2.com/?GlobalVariablesAreBad lub w celu uzyskania bardziej ogólnego wyjaśnienia: en.wikipedia.org/wiki/Global_variable
BuvinJ
2
W MySQL @variables są globalne. Łatwo to potwierdzić. Ustaw jedną poza funkcją, a następnie oceń ją w jednej. I odwrotnie, ustaw jedną wewnątrz funkcji i oceń ją poza nią. Zobaczysz, że funkcja nie chroni zakresu takich. Stają na sobie nawzajem.
BuvinJ
1
Używając terminologii MySQL, @@GLOBALzmienne są jeszcze bardziej „globalne” i podstępne. Przechodzą przez sesje! @variablesmają „zakres sesji”, więc przynajmniej pozostają w ten sposób ograniczone. Jednak w każdym normalnym języku, który określa się mianem zakresu „globalnego” (gdy przekraczają funkcje itp.). Pojęcie „globalne” w MySQL należy chyba nazwać „uniwersalnym”, ponieważ wykracza ono poza granice procesu, w którym jest uruchomiony. „Globalny” zwykle nie może tego zrobić w standardowym języku, ponieważ procesy nie współużytkują przestrzeni pamięci. Wynika to z trwałej (niestabilnej) tendencji SQL.
BuvinJ