分配变量时是SET还是SELECT?


Answers:


411

Quote本文总结如下:

  1. SET是用于变量分配的ANSI标准,而SELECT不是。
  2. SET一次只能分配一个变量,SELECT可以一次进行多个分配。
  3. 如果从查询分配,则SET只能分配标量值。如果查询返回多个值/行,则SET将引发错误。SELECT会将值之一分配给变量,并隐藏返回多个值的事实(因此您很可能永远都不知道为什么其他地方出了问题-对该故障排除进行有趣的故障排除)
  4. 从查询分配时,如果没有返回值,则SET将分配NULL,而SELECT根本不会进行赋值(因此该变量将不会从其先前的值更改)
  5. 至于速度差异-SET和SELECT之间没有直接差异。但是,SELECT一次完成多个任务的能力确实使其速度比SET稍有优势。

3
我没有投票,但是以下内容不太正确:“就速度差异而言-SET和SELECT之间没有直接差异”。如果您在一个选区中分配多个值,则可以比通过maultiple集更快。Google up“使用一个SELECT分配多个变量可以更快地工作”
AK 2010年

14
@AlexKuznetsov:之后的句子恰好说明了这一点。
OMG小马

3
@OMG小马:可以快10倍或更多,所以我不确定这是否是“轻微速度优势”。
AK 2010年

2
尤其是在使用While-Loop时,通过使用单选与多集设置/重新初始化我的所有变量,我已经看到了巨大的性能提升。我也可以将“变量逻辑”整合到“选择”中,以一次全部运行:示例:SELECT @Int = @Int + 1, @Int = @Int + 1如果@Int从0开始,然后以2结尾。这在进行连续的字符串操作时非常有用。
MikeTeeVee 2015年

关于性能差异的有趣讨论。如果通过select设置多个值的速度更快(编码和执行),则一种避免点4(如果查询返回null则变量值无法更改)的故障保护方法是在选择之前将变量显式设置为null。一旦考虑到这一点,两者如何比较性能?(旁注:我不了解在查询返回null的情况下选择不将变量设置为null的理由。您何时会想要?)
youcantryreachingme

155

我相信这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' */

4
+1最好只跑一次,以便理解,检查,玩耍,记住只是阅读而其他答案只是文字
Gennady VaninГеннадийВанин10年

4
如果您实际使用过select @var = (select name from master.sys.tables where name = 'qwerty'),则将@var设置为null。您提供的示例是不同的查询。
扎克2015年

4
您有(select name from master.sys.tables where name = 'qwerty')一个,而name from master.sys.tables where name = 'qwerty'另一个...您看不到吗?
Zack

4
@Zack:每种是我尝试演示的正确语法;当基础查询不返回任何结果时,使用SET与SELECT为变量分配值之间的区别。
Joe Stefanelli 2015年

5
(select name from master.sys.tables where name = 'qwerty')是标量子查询,并且name from master.sys.tables where name = 'qwerty'是简单查询。尽管您似乎暗示他们应该这样做,但两个不同的表达式不应产生相同的结果。如果您试图说SETand SELECT关键字具有不同的实现,则不应在示例中使用两个不同的表达式msdn.microsoft.com/en-us/library/ms187330.aspx
Zack

27

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

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*/

非常好,简洁
SimplyInk

8

除了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; 

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

现在让我们尝试使用SET

 -- Act
 set @employeeId = (select e.EmployeeId from dbo.Employee e);

您将收到一个错误:

子查询返回的值超过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;

清理

drop table Employee;

总之,使用:

  • SET:当您要为变量分配单个值并且您的变量用于单个值时。
  • SELECT:要为变量分配多个值时。该变量可以是表,临时表或表变量等。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.