【问题标题】:How to assign a select result to a variable?如何将选择结果分配给变量?
【发布时间】:2010-10-22 21:42:19
【问题描述】:

如何将选定的字段值存储到查询中的变量中并在更新语句中使用?

这是我的程序:

我正在编写一个 SQL Server 2005 T-SQL 存储过程,它执行以下操作:

  1. 从发票表中获取发票 ID 列表并存储到光标
  2. 从光标中获取发票 ID -> tmp_key 变量
  3. foreach tmp_key 从客户表中查找发票客户主要联系人 ID
  4. 使用主要联系人 ID 更新客户端联系人密钥
  5. 关闭光标

这是我的代码:

DECLARE @tmp_key int
DECLARE @get_invckey cursor 

set @get_invckey = CURSOR FOR 
    select invckey from tarinvoice where confirmtocntctkey is null and tranno like '%115876'

OPEN @get_invckey 

FETCH NEXT FROM @get_invckey into @tmp_key

WHILE (@@FETCH_STATUS = 0) 
BEGIN 
    SELECT c.PrimaryCntctKey as PrimaryContactKey
    from tarcustomer c, tarinvoice i
    where i.custkey = c.custkey and i.invckey = @tmp_key

    UPDATE tarinvoice set confirmtocntctkey = PrimaryContactKey where invckey = @tmp_key
    FETCH NEXT FROM @get_invckey INTO @tmp_key
END 

CLOSE @get_invckey
DEALLOCATE @get_invckey

如何存储 PrimaryContactKey 并在以下更新语句的 set 子句中再次使用它?我是创建一个游标变量还是创建另一个 int 类型的局部变量?

【问题讨论】:

  • 正如@GilaMonster 在下面回答的那样,整个操作可以是单个 UPDATE 语句(“基于集合的操作”,不要与 t-sql SET statement 混淆)这是一种更好的方法(更快执行,更少的开销和显着更少的代码)。我只是指出这一点,因为问题和所有当前的热门答案都是关于如何编写 SET 语句,但这确实不是最好的方法。

标签: tsql database-cursor


【解决方案1】:

我刚遇到同样的问题,然后...

declare @userId uniqueidentifier
set @userId = (select top 1 UserId from aspnet_Users)

甚至更短:

declare @userId uniqueidentifier
SELECT TOP 1 @userId = UserId FROM aspnet_Users

【讨论】:

  • 哈哈,我喜欢这个。分配标量值应该非常简单。讨厌游标 bla3.. 幸运的是,谷歌搜索找到了这个小答案。
  • 我不知道因为set @userId = (select top 1 UserId from aspnet_Users) 没有括号会导致“select附近的语法错误”!
  • 这个论坛展示了top的正确方法:sqlservercentral.com/Forums/Topic496124-169-1.aspx
  • 注意,上面的光标的使用与如何赋值给变量的问题无关。不幸的是,这使这个问题变得令人困惑。
  • 当心使用 TOP,因为 SQL Server 可能会在面临许多查询压力时重新排列行以进行性能调整。 第一行可能并不总是相同。
【解决方案2】:
DECLARE @tmp_key int
DECLARE @get_invckey cursor 

SET @get_invckey = CURSOR FOR 
    SELECT invckey FROM tarinvoice WHERE confirmtocntctkey IS NULL AND tranno LIKE '%115876'

OPEN @get_invckey 

FETCH NEXT FROM @get_invckey INTO @tmp_key

DECLARE @PrimaryContactKey int --or whatever datatype it is

WHILE (@@FETCH_STATUS = 0) 
BEGIN 
    SELECT @PrimaryContactKey=c.PrimaryCntctKey
    FROM tarcustomer c, tarinvoice i
    WHERE i.custkey = c.custkey AND i.invckey = @tmp_key

    UPDATE tarinvoice SET confirmtocntctkey = @PrimaryContactKey WHERE invckey = @tmp_key
    FETCH NEXT FROM @get_invckey INTO @tmp_key
END 

CLOSE @get_invckey
DEALLOCATE @get_invckey

编辑:
这个问题比我预期的要多得多。请注意,我并不是提倡在回答中使用光标,而是展示如何根据问题分配值。

【讨论】:

    【解决方案3】:

    试试这个

    SELECT @PrimaryContactKey = c.PrimaryCntctKey
    FROM tarcustomer c, tarinvoice i
    WHERE i.custkey = c.custkey 
        AND i.invckey = @tmp_key
    
    UPDATE tarinvoice SET confirmtocntctkey = @PrimaryContactKey 
    WHERE invckey = @tmp_key
    FETCH NEXT FROM @get_invckey INTO @tmp_key
    

    您可以在循环之外将此变量声明为标准 TSQL 变量。

    我还应该注意,这是您对任何类型的选择到变量中的方法,而不仅仅是在处理游标时。

    【讨论】:

      【解决方案4】:

      为什么需要光标? 您的整个代码段都可以用它来替换,这将在大量行上运行得更快。

      UPDATE tarinvoice set confirmtocntctkey = PrimaryCntctKey 
      FROM tarinvoice INNER JOIN tarcustomer ON tarinvoice.custkey = tarcustomer.custkey
      WHERE confirmtocntctkey is null and tranno like '%115876'
      

      【讨论】:

      • 光标真的不受欢迎吗?
      • 他们很慢。 SQL Server 针对基于集合的查询进行了优化。它在一次查询中对一百万行进行操作比对一行操作一百万次要快。再加上游标的开销,您通过使用游标而不是基于集合的操作来要求主要的性能问题测试您的游标解决方案和我的查询,看看两者的执行时间是多少。
      • 谢谢大哥!!!!这为我做了这件事,也比弄清楚如何为我的特定场景更改光标更容易。你是冠军!
      【解决方案5】:

      为了安全地分配变量,您必须使用 SET-SELECT 语句:

      SET @PrimaryContactKey = (SELECT c.PrimaryCntctKey
          FROM tarcustomer c, tarinvoice i
          WHERE i.custkey = c.custkey 
          AND i.invckey = @tmp_key)
      

      确保你有一个开始和一个结束括号!

      SET-SELECT 版本是设置变量的最安全方法的原因有两个。

      1. SELECT 返回多个帖子

      如果以下选择导致多个帖子会发生什么情况?

      SELECT @PrimaryContactKey = c.PrimaryCntctKey
      FROM tarcustomer c, tarinvoice i
      WHERE i.custkey = c.custkey 
          AND i.invckey = @tmp_key
      

      @PrimaryContactKey 将被分配结果中最后一个帖子的值。

      事实上,@PrimaryContactKey 将在结果中为每个帖子分配一个值,因此它将包含 SELECT 命令正在处理的最后一个帖子的值。

      哪个帖子是“最后一个”由任何聚集索引确定,或者,如果没有使用聚集索引或主键是聚集的,那么“最后一个”帖子将是最近添加的帖子。在最坏的情况下,每次更改表的索引时都可能更改此行为。

      使用 SET-SELECT 语句,您的变量将设置为 null

      2。 SELECT 不返回任何帖子

      在使用第二版代码时,如果您的选择根本不返回结果,会发生什么?

      与您可能认为的相反,变量的值不会为空 - 它会保留它以前的值!

      这是因为,如上所述,SQL 将为变量赋值每个帖子一次 - 这意味着如果结果不包含任何帖子,它不会对变量做任何事情。因此,变量仍将具有运行语句之前的值。

      使用 SET-SELECT 语句,值将是 null

      另请参阅:SET versus SELECT when assigning variables?

      【讨论】:

      • Erk 是正确的,这应该被标记为答案。第二点是最近让我着迷的……
      • 我刚刚用上面案例 1 的代码解决了一个错误。最初的编码员似乎并不关心选择了几行,但不幸的是,MS SQL Server 有时可能会以不同于插入的顺序返回行,除了优化之外没有其他明显的原因......结果是随机且令人困惑的......
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-12-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-02-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多