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

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

【问题讨论】:

    标签: sql sql-server sql-server-2005 tsql sql-server-2008


    【解决方案1】:

    Quote,总结自this article

    1. SET 是变量赋值的 ANSI 标准,SELECT 不是。
    2. SET 一次只能分配一个变量,SELECT 可以一次进行多个分配。
    3. 如果从查询中分配,SET 只能分配一个标量值。如果查询返回多个值/行,则 SET 将引发错误。 SELECT 会将其中一个值分配给变量并隐藏返回多个值的事实(因此您可能永远不知道为什么其他地方出了问题 - 有乐趣解决那个问题)
    4. 当从查询中赋值时,如果没有返回值,则 SET 将赋值为 NULL,而 SELECT 根本不会进行赋值(因此变量不会从之前的值更改)
    5. 就速度差异而言 - 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:当你想为一个变量分配多个值时。变量可以是表、临时表或表变量等。

    【讨论】:

      猜你喜欢
      • 2018-12-10
      • 2015-08-03
      • 2020-03-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-09-29
      • 2019-06-30
      • 1970-01-01
      相关资源
      最近更新 更多