Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
Applies to:
SQL Server
Azure SQL Database
Azure SQL Managed Instance
Azure Synapse Analytics
Analytics Platform System (PDW)
SQL analytics endpoint in Microsoft Fabric
Warehouse in Microsoft Fabric
SQL database in Microsoft Fabric Preview
Variables are declared in the body of a batch or procedure with the DECLARE
statement and are assigned values by using either a SET
or SELECT
statement. Cursor variables can be declared with this statement and used with other cursor-related statements. After declaration, all variables are initialized as NULL
, unless a value is provided as part of the declaration.
Transact-SQL syntax conventions
Syntax
The following syntax is for SQL Server and Azure SQL Database:
DECLARE
{
{ @local_variable [AS] data_type [ = value ] }
| { @cursor_variable_name CURSOR }
| { @table_variable_name [AS] <table_type_definition> }
} [ , ...n ]
<table_type_definition> ::=
TABLE ( { <column_definition> | <table_constraint> | <table_index> } } [ , ...n ] )
<column_definition> ::=
column_name { scalar_data_type | AS computed_column_expression }
[ COLLATE collation_name ]
[ [ DEFAULT constant_expression ] | IDENTITY [ (seed, increment ) ] ]
[ ROWGUIDCOL ]
[ <column_constraint> ]
[ <column_index> ]
<column_constraint> ::=
{
[ NULL | NOT NULL ]
{ PRIMARY KEY | UNIQUE }
[ CLUSTERED | NONCLUSTERED ]
[ WITH FILLFACTOR = fillfactor
| WITH ( < index_option > [ , ...n ] )
[ ON { filegroup | "default" } ]
| [ CHECK ( logical_expression ) ] [ , ...n ]
}
<column_index> ::=
INDEX index_name [ CLUSTERED | NONCLUSTERED ]
[ WITH ( <index_option> [ , ... n ] ) ]
[ ON { partition_scheme_name (column_name )
| filegroup_name
| default
}
]
[ FILESTREAM_ON { filestream_filegroup_name | partition_scheme_name | "NULL" } ]
<table_constraint> ::=
{
{ PRIMARY KEY | UNIQUE }
[ CLUSTERED | NONCLUSTERED ]
( column_name [ ASC | DESC ] [ , ...n ]
[ WITH FILLFACTOR = fillfactor
| WITH ( <index_option> [ , ...n ] )
| [ CHECK ( logical_expression ) ] [ , ...n ]
}
<table_index> ::=
{
{
INDEX index_name [ UNIQUE ] [ CLUSTERED | NONCLUSTERED ]
(column_name [ ASC | DESC ] [ , ... n ] )
| INDEX index_name CLUSTERED COLUMNSTORE
| INDEX index_name [ NONCLUSTERED ] COLUMNSTORE ( column_name [ , ... n ] )
}
[ WITH ( <index_option> [ , ... n ] ) ]
[ ON { partition_scheme_name ( column_name )
| filegroup_name
| default
}
]
[ FILESTREAM_ON { filestream_filegroup_name | partition_scheme_name | "NULL" } ]
}
<index_option> ::=
{
PAD_INDEX = { ON | OFF }
| FILLFACTOR = fillfactor
| IGNORE_DUP_KEY = { ON | OFF }
| STATISTICS_NORECOMPUTE = { ON | OFF }
| STATISTICS_INCREMENTAL = { ON | OFF }
| ALLOW_ROW_LOCKS = { ON | OFF }
| ALLOW_PAGE_LOCKS = { ON | OFF }
| OPTIMIZE_FOR_SEQUENTIAL_KEY = { ON | OFF }
| COMPRESSION_DELAY = { 0 | delay [ Minutes ] }
| DATA_COMPRESSION = { NONE | ROW | PAGE | COLUMNSTORE | COLUMNSTORE_ARCHIVE }
[ ON PARTITIONS ( { partition_number_expression | <range> }
[ , ...n ] ) ]
| XML_COMPRESSION = { ON | OFF }
[ ON PARTITIONS ( { <partition_number_expression> | <range> }
[ , ...n ] ) ] ]
}
The following syntax is for Azure Synapse Analytics and Parallel Data Warehouse and Microsoft Fabric:
DECLARE
{ { @local_variable [AS] data_type } [ = value [ COLLATE <collation_name> ] ] } [ , ...n ]
Arguments
@local_variable
The name of a variable. Variable names must begin with an at (@) sign. Local variable names must comply with the rules for identifiers.
data_type
Any system-supplied, common language runtime (CLR) user-defined table type, or alias data type. A variable can't be of text, ntext, or image data type.
For more information about system data types, see Data types. For more information about CLR user-defined types or alias data types, see CREATE TYPE.
= value
Assigns a value to the variable in-line. The value can be a constant or an expression, but it must either match the variable declaration type or be implicitly convertible to that type. For more information, see Expressions.
@cursor_variable_name
The name of a cursor variable. Cursor variable names must begin with an at (@) sign and conform to the rules for identifiers.
CURSOR
Specifies that the variable is a local cursor variable.
@table_variable_name
The name of a variable of type table. Variable names must begin with an at (@) sign and conform to the rules for identifiers.
<table_type_definition>
Defines the table data type. The table declaration includes column definitions, names, data types, and constraints. The only constraint types allowed are
PRIMARY KEY
,UNIQUE
,NULL
, andCHECK
. An alias data type can't be used as a column scalar data type if a rule or default definition is bound to the type.
<table_type_definition>
A subset of information used to define a table in CREATE TABLE
. Elements and essential definitions are included here. For more information, see CREATE TABLE.
n
A placeholder indicating that multiple variables can be specified and assigned values. When declaring table variables, the table variable must be the only variable being declared in the
DECLARE
statement.
column_name
The name of the column in the table.
scalar_data_type
Specifies that the column is a scalar data type.
computed_column_expression
An expression defining the value of a computed column. It's computed from an expression using other columns in the same table. For example, a computed column can have the definition
cost AS price * qty
. The expression can be a noncomputed column name, constant, built-in function, variable, or any combination of these options connected by one or more operators. The expression can't be a subquery or a user-defined function. The expression can't reference a CLR user-defined type.
[ COLLATE collation_name ]
Specifies the collation for the column. collation_name can be either a Windows collation name or a SQL collation name, and is applicable only for columns of the char, varchar, text, nchar, nvarchar, and ntext data types. If not specified, the column is assigned either the collation of the user-defined data type (if the column is of a user-defined data type) or the collation of the current database.
For more information about the Windows and SQL collation names, see COLLATE.
DEFAULT
Specifies the value provided for the column when a value isn't explicitly supplied during an insert. DEFAULT
definitions can be applied to any columns, except columns defined as timestamp or with the IDENTITY
property. DEFAULT
definitions are removed when the table is dropped. Only a constant value, such as a character string; a system function, such as a SYSTEM_USER()
; or NULL
can be used as a default. To maintain compatibility with earlier versions of SQL Server, a constraint name can be assigned to a DEFAULT
.
constant_expression
A constant,
NULL
, or a system function used as the default value for the column.
IDENTITY
Indicates that the new column is an identity column. When a new row is added to the table, SQL Server provides a unique incremental value for the column. Identity columns are commonly used with PRIMARY KEY
constraints to serve as the unique row identifier for the table. The IDENTITY
property can be assigned to tinyint, smallint, int, decimal(p,0), or numeric(p,0) columns. Only one identity column can be created per table. Bound defaults and DEFAULT
constraints can't be used with an identity column. You must specify both the seed and increment, or neither. If neither is specified, the default is (1,1).
seed
The value used for the first row loaded into the table.
increment
The incremental value added to the identity value of the previous row that was loaded.
ROWGUIDCOL
Indicates that the new column is a row global unique identifier column. Only one uniqueidentifier column per table can be designated as the ROWGUIDCOL
column. The ROWGUIDCOL
property can be assigned only to a uniqueidentifier column.
NULL | NOT NULL
Indicates if null is allowed in the variable. The default is NULL
.
PRIMARY KEY
A constraint that enforces entity integrity for a given column or columns through a unique index. Only one PRIMARY KEY
constraint can be created per table.
UNIQUE
A constraint that provides entity integrity for a given column or columns through a unique index. A table can have multiple UNIQUE
constraints.
CLUSTERED | NONCLUSTERED
Indicate that a clustered or a nonclustered index is created for the PRIMARY KEY
or UNIQUE
constraint. PRIMARY KEY
constraints use CLUSTERED
, and UNIQUE
constraints use NONCLUSTERED
.
CLUSTERED
can be specified for only one constraint. If CLUSTERED
is specified for a UNIQUE
constraint and a PRIMARY KEY
constraint is also specified, the PRIMARY KEY
uses NONCLUSTERED
.
CHECK
A constraint that enforces ___domain integrity by limiting the possible values that can be entered into a column or columns.
logical_expression
A logical expression that returns
TRUE
orFALSE
.
<index_option>
Specifies one or more index options. Indexes can't be created explicitly on table variables, and no statistics are kept on table variables. SQL Server 2014 (12.x) introduced syntax that allows you to create certain index types inline with the table definition. Using this syntax, you can create indexes on table variables as part of the table definition. In some cases, performance might improve by using temporary tables instead, which provide full index support and statistics.
For a complete description of these options, see CREATE TABLE.
Table variables and row estimates
Table variables don't have distribution statistics. In many cases, the optimizer builds a query plan on the assumption that the table variable has zero rows or one row. For more information, review table data type - Limitations and restrictions.
For this reason, you should be cautious about using a table variable if you expect a larger number of rows (greater than 100). Consider the following alternatives:
Temp tables might be a better solution than table variables when it's possible for the rowcount to be larger (greater than 100).
For queries that join the table variable with other tables, use the
RECOMPILE
hint, which causes the optimizer to use the correct cardinality for the table variable.In Azure SQL Database and starting with SQL Server 2019 (15.x), the table variable deferred compilation feature propagates cardinality estimates that are based on actual table variable row counts, providing a more accurate row count for optimizing the execution plan. For more information, see Intelligent query processing in SQL databases.
Remarks
Variables are often used in a batch or procedure as counters for WHILE
, LOOP
, or for an IF...ELSE
block.
Variables can be used only in expressions, not in place of object names or keywords. To construct dynamic SQL statements, use EXECUTE
.
The scope of a local variable is the batch in which it's declared.
A table variable isn't necessarily memory resident. Under memory pressure, the pages belonging to a table variable can be pushed out to tempdb
.
You can define an inline index in a table variable.
A cursor variable that currently has a cursor assigned to it can be referenced as a source in a:
CLOSE
statementDEALLOCATE
statementFETCH
statementOPEN
statement- Positioned
DELETE
orUPDATE
statement SET CURSOR
variable statement (on the right side)
In all of these statements, SQL Server raises an error if a referenced cursor variable exists but doesn't have a cursor currently allocated to it. If a referenced cursor variable doesn't exist, SQL Server raises the same error raised for an undeclared variable of another type.
A cursor variable:
Can be the target of either a cursor type or another cursor variable. For more information, see SET @local_variable.
Can be referenced as the target of an output cursor parameter in an
EXECUTE
statement if the cursor variable doesn't have a cursor currently assigned to it.Should be regarded as a pointer to the cursor.
Examples
The code samples in this article use the AdventureWorks2022
or AdventureWorksDW2022
sample database, which you can download from the Microsoft SQL Server Samples and Community Projects home page.
A. Use DECLARE
The following example uses a local variable named @find
to retrieve contact information for all family names beginning with Man
.
USE AdventureWorks2022;
GO
DECLARE @find AS VARCHAR (30);
/* Also allowed:
DECLARE @find VARCHAR(30) = 'Man%';
*/
SET @find = 'Man%';
SELECT p.LastName,
p.FirstName,
ph.PhoneNumber
FROM Person.Person AS p
INNER JOIN Person.PersonPhone AS ph
ON p.BusinessEntityID = ph.BusinessEntityID
WHERE LastName LIKE @find;
Here's the result set.
LastName FirstName Phone
------------------- ----------------------- -------------------------
Manchepalli Ajay 1 (11) 500 555-0174
Manek Parul 1 (11) 500 555-0146
Manzanares Tomas 1 (11) 500 555-0178
B. Use DECLARE with two variables
The following example retrieves the names of Adventure Works Cycles sales representatives who are located in the North American sales territory and have at least $2,000,000 in sales for the year.
USE AdventureWorks2022;
GO
SET NOCOUNT ON;
GO
DECLARE @Group AS NVARCHAR (50), @Sales AS MONEY;
SET @Group = N'North America';
SET @Sales = 2000000;
SET NOCOUNT OFF;
SELECT FirstName,
LastName,
SalesYTD
FROM Sales.vSalesPerson
WHERE TerritoryGroup = @Group
AND SalesYTD >= @Sales;
C. Declare a variable of type table
The following example creates a table
variable that stores the values specified in the OUTPUT
clause of the UPDATE
statement. Two SELECT
statements follow that return the values in @MyTableVar
and the results of the update operation in the Employee
table. The results in the INSERTED.ModifiedDate
column differ from the values in the ModifiedDate
column in the Employee
table. This is because the AFTER UPDATE
trigger, which updates the value of ModifiedDate
to the current date, is defined on the Employee
table. However, the columns returned from OUTPUT
reflect the data before triggers are fired. For more information, see OUTPUT clause.
USE AdventureWorks2022;
GO
DECLARE @MyTableVar TABLE (
EmpID INT NOT NULL,
OldVacationHours INT,
NewVacationHours INT,
ModifiedDate DATETIME);
UPDATE TOP (10) HumanResources.Employee
SET VacationHours = VacationHours * 1.25
OUTPUT INSERTED.BusinessEntityID,
DELETED.VacationHours,
INSERTED.VacationHours,
INSERTED.ModifiedDate
INTO @MyTableVar;
--Display the result set of the table variable.
SELECT EmpID,
OldVacationHours,
NewVacationHours,
ModifiedDate
FROM @MyTableVar;
GO
--Display the result set of the table.
--Note that ModifiedDate reflects the value generated by an
--AFTER UPDATE trigger.
SELECT TOP (10) BusinessEntityID,
VacationHours,
ModifiedDate
FROM HumanResources.Employee;
GO
D. Declare a variable of type table, with inline indexes
The following example creates a table
variable with a clustered inline index and two nonclustered inline indexes.
DECLARE @MyTableVar TABLE (
EmpID INT NOT NULL,
PRIMARY KEY CLUSTERED (EmpID),
UNIQUE NONCLUSTERED (EmpID),
INDEX CustomNonClusteredIndex NONCLUSTERED (EmpID));
GO
The following query returns information about the indexes created in the previous query.
SELECT * FROM tempdb.sys.indexes
WHERE object_id < 0;
GO
E. Declare a variable of user-defined table type
The following example creates a table-valued parameter or table variable called @LocationTVP
. This step requires a corresponding user-defined table type called LocationTableType
.
For more information about how to create a user-defined table type, see CREATE TYPE. For more information about table-valued parameters, see Use table-valued parameters (Database Engine).
DECLARE @LocationTVP AS LocationTableType;
Examples: Azure Synapse Analytics and Analytics Platform System (PDW)
F. Use DECLARE
The following example uses a local variable named @find
to retrieve contact information for all family names beginning with Walt
.
-- Uses AdventureWorks
DECLARE @find AS VARCHAR (30);
/* Also allowed:
DECLARE @find VARCHAR(30) = 'Man%';
*/
SET @find = 'Walt%';
SELECT LastName,
FirstName,
Phone
FROM DimEmployee
WHERE LastName LIKE @find;
G. Use DECLARE with two variables
The following example retrieves uses variables to specify the first and family names of employees in the DimEmployee
table.
DECLARE @lastName AS VARCHAR (30),
@firstName AS VARCHAR (30);
SET @lastName = 'Walt%';
SET @firstName = 'Bryan';
SELECT LastName,
FirstName,
Phone
FROM DimEmployee
WHERE LastName LIKE @lastName
AND FirstName LIKE @firstName;