分配变量时设置与选择?

Posted

技术标签:

【中文标题】分配变量时设置与选择?【英文标题】:SET versus SELECT when assigning variables? 【发布时间】:2011-04-26 02:42:29 【问题描述】:

在T-SQL中赋值变量时SETSELECT语句有什么区别?

【问题讨论】:

【参考方案1】:

Quote,总结自this article:

    SET 是变量赋值的 ANSI 标准,SELECT 不是。 SET 一次只能分配一个变量,SELECT 可以一次进行多个分配。 如果从查询中分配,SET 只能分配一个标量值。如果查询返回多个值/行,则 SET 将引发错误。 SELECT 会将其中一个值分配给变量并隐藏返回多个值的事实(因此您可能永远不知道为什么其他地方出了问题 - 有乐趣解决那个问题) 当从查询中赋值时,如果没有返回值,则 SET 将赋值为 NULL,而 SELECT 根本不会进行赋值(因此变量不会从之前的值更改) 就速度差异而言 - SET 和 SELECT 之间没有直接差异。不过,SELECT 一次完成多项任务的能力确实比 SET 稍有速度优势。

【讨论】:

我没有投反对票,但以下内容并不完全正确:“就速度差异而言 - SET 和 SELECT 之间没有直接差异”。如果您在一个 slect 中分配多个值,则可以比通过多个集合快得多。谷歌起来“用一个 SELECT 分配多个变量工作得更快” @AlexKuznetsov:后面的那句话就是这么说的。 @OMG Ponies:可以快10倍甚至更多,所以我不确定是不是“轻微的速度优势”。 特别是在使用 While-Loop 时,通过使用单选与多集设置/重新初始化所有变量,我看到了巨大的性能提升。我也可以将我的变量逻辑合并到一个 Select 中以一次全部运行:示例:SELECT @Int = @Int + 1, @Int = @Int + 1,如果@Int 以 0 开始,则以 2 结束。这在进行连续的字符串操作时非常有用。 关于性能差异的有趣讨论。如果通过 select 设置多个值更快(编码和执行),那么避免第 4 点(如果查询返回 null,您的变量值无法更改)的一种故障安全方法是在 select 之前将您的变量显式设置为 null。一旦考虑到这一点,两者的性能如何比较? (旁注:如果查询返回 null,我不明白 select 不将变量设置为 null 的理由。你什么时候想要那个?)【参考方案2】:

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

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

【讨论】:

+1 最好跑一次,以便理解、检查、玩、记住,只是阅读,其他答案只是文字 如果你实际使用了select @var = (select name from master.sys.tables where name = 'qwerty'),你会得到@var为空。您给出的示例不是同一个查询。 你有一个(select name from master.sys.tables where name = 'qwerty'),另一个是name from master.sys.tables where name = 'qwerty'...你没看到吗? @Zack:每个都是我尝试演示的正确语法;当底层查询不返回结果时,使用 SET 与 SELECT 为变量赋值的区别。 (select name from master.sys.tables where name = 'qwerty') 是标量子查询,name from master.sys.tables where name = 'qwerty' 是简单查询。这两个不同的 表达式 不应该产生相同的结果,尽管您似乎暗示它们应该产生相同的结果。如果你想说SETSELECT 关键字有不同的实现,你不应该在你的例子中使用两个不同的表达式。 msdn.microsoft.com/en-us/library/ms187330.aspx【参考方案3】:

在编写查询时,应牢记这一区别:

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

【讨论】:

非常好,简洁【参考方案4】:

除了 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:当你想为一个变量分配多个值时。变量可以是表、临时表或表变量等。

【讨论】:

以上是关于分配变量时设置与选择?的主要内容,如果未能解决你的问题,请参考以下文章

如何将随机设置的单词值分配给变量 Javascript? (不和谐 JS v12H

北理工c语言单项选择题

分配错误之前引用的Python局部变量

C++中2道结构方面选择题,详细解释一下 当定义一个结构体变量时,系统为它分配的内存空间是

如何将选择的结果分配给 sqlplus 变量

尝试选择节点时如何在 xsl 中使用变量?