Como atribuir um resultado de seleção para uma variável?
Pergunta
Como faço para armazenar um valor campo selecionado em uma variável de uma consulta e usá-lo em uma instrução de atualização?
Aqui está o meu procedimento:
Eu estou escrevendo um procedimento armazenado 2005 T-SQL SQL Server que faz o seguinte:
- recebe lista de facturas id é da tabela factura e lojas para Cursor
- Obter id factura de cursor -> variável tmp_key
- achados foreach tmp_key factura cliente ID de contato primário da tabela do cliente
- atualiza a chave de contato do cliente com ID de contato primário
- close cursor
Aqui está o meu código:
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
Como faço para armazenar o PrimaryContactKey e usá-lo novamente na cláusula set da seguinte declaração atualização? Faço para criar uma variável de cursor ou apenas uma outra variável local com um tipo int?
Solução
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
EDIT:
Esta questão tem obtido um tração muito mais do que eu teria antecipado. Note que não estou defendendo o uso do cursor na minha resposta, mas sim mostrando como atribuir o valor com base na pergunta.
Outras dicas
Eu apenas tive o mesmo problema e ...
declare @userId uniqueidentifier
set @userId = (select top 1 UserId from aspnet_Users)
ou ainda mais curto:
declare @userId uniqueidentifier
SELECT TOP 1 @userId = UserId FROM aspnet_Users
Tente este
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
Você iria declarar esta fora variável do seu ciclo como apenas uma variável TSQL padrão.
Gostaria também de salientar que esta é a forma como você faria para qualquer tipo de seleção em uma variável, não apenas quando se lida com cursores.
Por que você precisa de um cursor em tudo? Todo o seu segmento de código pode ser substituída pela presente, que vai correr muito mais rápido em um grande número de linhas.
UPDATE tarinvoice set confirmtocntctkey = PrimaryCntctKey
FROM tarinvoice INNER JOIN tarcustomer ON tarinvoice.custkey = tarcustomer.custkey
WHERE confirmtocntctkey is null and tranno like '%115876'
Para atribuir uma variável com segurança você tem que usar a instrução SET-SELECT:
SET @PrimaryContactKey = (SELECT c.PrimaryCntctKey
FROM tarcustomer c, tarinvoice i
WHERE i.custkey = c.custkey
AND i.invckey = @tmp_key)
Certifique-se de que você tem tanto uma partida e um parêntese final!
A razão a versão SET SELECT é a forma mais segura para definir uma variável é duplo.
1. Os retornos Seleccione várias mensagens
O que acontece se os seguintes resultados selecionados em vários lugares?
SELECT @PrimaryContactKey = c.PrimaryCntctKey
FROM tarcustomer c, tarinvoice i
WHERE i.custkey = c.custkey
AND i.invckey = @tmp_key
@PrimaryContactKey
será atribuído o valor do último post no resultado.
Na verdade @PrimaryContactKey
será atribuído um valor por posto no resultado, por isso vai, consequentemente, conter o valor do último post do SELECT-comando estava processando.
Qual post é "passado" é determinada por quaisquer índices de cluster ou, se nenhum índice agrupado é usado ou a chave primária está agrupado, o "último" post será o post mais recentemente adicionado. Este comportamento poderia, na pior das hipóteses, ser alterada cada vez que a indexação da tabela é alterado.
Com uma declaração SET SELECT sua variável será definido para null
.
2. O SELECT retorna há mensagens
O que acontece, quando se utiliza a segunda versão do código, se a sua escolha não retorna um resultado em tudo?
Em um ao contrário do que você pode acreditar que o valor da variável não será nulo - ela irá reter é o valor anterior
Isto porque, como dito acima, o SQL irá atribuir um valor à variável uma vez por mensagem - o que significa que não vai fazer nada com a variável se o resultado não contém mensagens. Assim, a variável ainda terá o valor que tinha antes de executar a declaração.
Com a instrução SET-selecionar o valor será null
.
Veja também: contra SELECT quando a atribuição de variáveis ??