分配变量时SET与SELECT?

juu*_*uur 275 sql t-sql sql-server sql-server-2005 sql-server-2008

在T-SQL中分配变量时,SETSELECT语句之间有什么区别?

OMG*_*ies 401

引用,总结来自这篇文章:

  1. SET是变量赋值的ANSI标准,SELECT不是.
  2. SET一次只能分配一个变量,SELECT可以一次分配多个赋值.
  3. 如果从查询中分配,SET只能分配标量值.如果查询返回多个值/行,则SET将引发错误.SELECT会将其中一个值分配给变量,并隐藏返回多个值的事实(因此您可能永远不会知道其他地方出现问题的原因 - 请对其进行故障排除)
  4. 从查询中分配时,如果没有返回值,则SET将分配NULL,其中SELECT将根本不进行赋值(因此变量不会从其先前的值更改)
  5. 就速度差异而言 - SET和SELECT之间没有直接的区别.然而,SELECT能够在一次拍摄中进行多次分配确实比SET具有轻微的速度优势.

  • @AlexKuznetsov:之后的句子就是这么说的. (14认同)
  • 我没有投票,但以下不太正确:"就速度差异而言 - SET和SELECT之间没有直接差异".如果在一个slect中分配多个值,则通过多个集合可以快得多.Google up"使用一个SELECT分配多个变量的速度更快" (3认同)
  • @OMG小马:它可以快10倍或更多,所以我不确定它是否是"轻微的速度优势". (3认同)
  • 特别是在使用While-Loop时,通过使用单选与多集设置/重新初始化我的所有变量,我已经看到了巨大的性能提升。我还可以将“变量逻辑”整合到“选择”中,使其也一次运行:例如:SELECT @Int = @Int + 1,@Int = @Int + 1,如果`@ Int`开始为0,则结束于2。这在进行连续的字符串操作时非常有用。 (2认同)

Joe*_*lli 150

我相信SET是ANSI标准,而SELECT不是.另请注意,未找到值时,以下示例中的SETvs的行为不同SELECT.

declare @var varchar(20)
set @var = 'Joe'
set @var = (select name from master.sys.tables where name = 'qwerty')
select @var /* @var is now NULL */

set @var = 'Joe'
select @var = name from master.sys.tables where name = 'qwerty'
select @var /* @var is still equal to 'Joe' */
Run Code Online (Sandbox Code Playgroud)

  • @Zack你完全错过了这个例子. (5认同)
  • `(从master.sys.tables中选择名称,其中name ='qwerty')`是一个标量子查询,`name来自master.sys.tables,其中name ='qwerty'`是一个简单的查询.两个不同的*表达式*不应该产生相同的结果,尽管看起来你暗示他们应该这样做.如果你试图说'SET`和`SELECT`关键字有不同的实现,你不应该在你的例子中使用两个不同的*表达式*.https://msdn.microsoft.com/en-us/library/ms187330.aspx (5认同)
  • +1最好运行一次以便理解,检查,播放,记忆只读,但其他答案只是文本 (4认同)
  • 如果你实际上使用`select @var =(从master.sys.tables中选择名称,其中name ='qwerty')`你将得到@var为null.您提供的示例不是同一个查询. (4认同)
  • 你有`(从master.sys.tables选择名称,其中name ='qwerty')`一个,而`name来自master.sys.tables,其中name ='qwerty'`为另一个...你看不到? (4认同)
  • @Zack:每个都是我试图演示的正确语法; 当底层查询没有返回结果时,使用SET与SELECT为值赋值. (4认同)
  • 你看到了不同的结果,因为你正在使用不同的查询......如果你的观点是当你使用`set`而不是`select`时显示同一个查询的行为不同那么你为什么要改变右边的内容每个例子中的`=`符号?如果你使用相同的东西,你会得到相同的结果. (3认同)

Gor*_*ulu 24

在编写查询时,应记住这种差异:

DECLARE @A INT = 2

SELECT  @A = TBL.A
FROM    ( SELECT 1 A ) TBL
WHERE   1 = 2

SELECT  @A
/* @A is 2*/

---------------------------------------------------------------

DECLARE @A INT = 2

SET @A = ( 
            SELECT  TBL.A
            FROM    ( SELECT 1 A) TBL
            WHERE   1 = 2
         )

SELECT  @A
/* @A is null*/
Run Code Online (Sandbox Code Playgroud)


Cod*_*shi 5

除了ANSI和速度等之外,还有一个非常重要的差异对我始终很重要;超过ANSI和速度。由于这一重要的疏忽,我修复的错误数量很多。我一直在代码审查期间寻找这个。

-- Arrange
create table Employee (EmployeeId int);
insert into dbo.Employee values (1);
insert into dbo.Employee values (2);
insert into dbo.Employee values (3);

-- Act
declare @employeeId int;
select @employeeId = e.EmployeeId from dbo.Employee e;

-- Assert
-- This will print 3, the last EmployeeId from the query (an arbitrary value)
-- Almost always, this is not what the developer was intending. 
print @employeeId; 
Run Code Online (Sandbox Code Playgroud)

几乎总是,这不是开发人员想要的。在上面的查询中,查询很简单,但是我看到的查询非常复杂,弄清楚它是否将返回单个值并非易事。查询通常比这更复杂,并且偶然地它一直在返回单个值。在开发人员测试期间,一切都很好。但这就像一枚滴答作响的炸弹,当查询返回多个结果时将引起问题。为什么?因为它将仅将最后一个值分配给变量。

现在让我们尝试使用SET

 -- Act
 set @employeeId = (select e.EmployeeId from dbo.Employee e);
Run Code Online (Sandbox Code Playgroud)

您将收到一个错误:

子查询返回的值超过1。当子查询遵循=,!=,<,<=,>,> =或将子查询用作表达式时,不允许这样做。

这是非常令人惊奇且非常重要的,因为您为什么要为分配一些琐碎的“结果中的最后一项” @employeeId。有了它,select您将永远不会收到任何错误,并且将花费数分钟,数小时进行调试。

也许,您正在寻找一个ID,这SET将迫使您修复查询。因此,您可以执行以下操作:

-- Act
-- Notice the where clause
set @employeeId = (select e.EmployeeId from dbo.Employee e where e.EmployeeId = 1);
print @employeeId;
Run Code Online (Sandbox Code Playgroud)

清理

drop table Employee;
Run Code Online (Sandbox Code Playgroud)

最后,使用:

  • SET:当您要为变量分配单个值并且您的变量用于单个值时。
  • SELECT:要为变量分配多个值时。该变量可以是表,临时表或表变量等。