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


73

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

这是我的程序:

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

  1. 从发票表中获取发票编号列表并将其存储到Cursor
  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类型的局部变量?


2
正如@GilaMonster在下面回答的那样,整个操作可以是单个UPDATE语句(“基于集合的操作”,不要与t-sql SET语句混淆),这是一种更好的方法(执行速度更快,开销更少,并且大大减少了代码)。我之所以指出这一点是因为问题和所有当前的主要答案都与如何编写SET语句有关,但实际上这并不是开始的最佳方法。
gregmac

Answers:


47
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

编辑:
这个问题比我预期的要吸引得多。请注意,我并不是在答案中主张使用游标,而是说明如何根据问题分配值。


103

我只是有同样的问题而...

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

甚至更短:

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

1
哈哈,我喜欢这个。分配标量值应该非常简单。讨厌光标bla3 ..运气好的谷歌搜索找到了这个小答案。
CallMeLaNN

3
我不知道,因为set @userId = (select top 1 UserId from aspnet_Users) 没有括号会导致“ select附近语法错误”!
CallMeLaNN

该论坛显示了正确的top方法:sqlservercentral.com/Forums/Topic496124-169-1.aspx
Chip McCormick

3
请注意,上面光标的使用与如何分配变量无关。不幸的是,这使这成为一个令人困惑的问题。
Chip McCormick

1
提防使用TOP,因为在遇到许多查询时,SQL Server可能会重新排列行以进行性能调整。第一行可能并不总是相同。
Erk

19

尝试这个

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变量。

我还应该注意,这是您对任何类型的select变量进行处理的方式,而不仅是在处理游标时。


14

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

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

光标真的皱眉了吗?
09年

4
太慢了 SQL Server针对基于集合的查询进行了优化。在一个查询中对一百万行进行操作要比对一百万行进行操作要快。加上游标的开销,您正在使用游标而不是基于集合的操作来询问主要的性能问题测试您的游标解决方案和我的查询,看看两者的执行时间是多少。
GilaMonster,2009年

谢啦!!!!这为我做到了,而且比找出如何为我的特定情况更改光标更容易。你是冠军!
艾伦·鲍尔

14

为了安全地分配变量,您必须使用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与SELECT相对?


1
Erk是正确的,应该将其标记为答案。第二点是最近吸引我的地方……
wexman

我只是按照上面的案例1解决了一个带有代码的错误。原始编码器似乎并不在乎是否选择了几行,但是不幸的是,MS SQL Server有时可能会以不同于插入的顺序返回行,其原因除了优化外没有其他明显的原因……结果是随机且令人困惑的……
2015年
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.