分配变量时设置与选择?
Posted
技术标签:
【中文标题】分配变量时设置与选择?【英文标题】:SET versus SELECT when assigning variables? 【发布时间】:2011-04-26 02:42:29 【问题描述】:在T-SQL中赋值变量时SET
和SELECT
语句有什么区别?
【问题讨论】:
【参考方案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
不是。另请注意以下示例中在未找到值时SET
与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' */
【讨论】:
+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'
是简单查询。这两个不同的 表达式 不应该产生相同的结果,尽管您似乎暗示它们应该产生相同的结果。如果你想说SET
和SELECT
关键字有不同的实现,你不应该在你的例子中使用两个不同的表达式。 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