web-dev-qa-db-fra.com

Comment assigner un résultat de sélection à une variable?

Comment enregistrer une valeur de champ sélectionnée dans une variable à partir d'une requête et l'utiliser dans une instruction de mise à jour?

Voici ma procédure:

J'écris une procédure stockée SQL Server 2005 T-SQL qui effectue les opérations suivantes:

  1. récupère la liste des identifiants de factures de la table de facturation et les stocke au curseur
  2. Récupérer l'identifiant de la facture du curseur -> Variable tmp_key
  3. foreach tmp_key recherche l'ID de contact principal du client de facturation à partir de la table client
  4. met à jour la clé de contact du client avec l'identifiant de contact principal
  5. fermer le curseur

Voici mon code:

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

Comment puis-je stocker la PrimaryContactKey et l'utiliser à nouveau dans la clause set de la déclaration de mise à jour suivante? Est-ce que je crée une variable de curseur ou juste une autre variable locale avec un type int?

70
phill
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:
Cette question a eu beaucoup plus de succès que je ne l'aurais cru. Notez que je ne préconise pas l'utilisation du curseur dans ma réponse, mais montre plutôt comment attribuer la valeur en fonction de la question.

44
squillman

Je viens d'avoir le même problème et ...

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

ou même plus court:

declare @userId uniqueidentifier
SELECT TOP 1 @userId = UserId FROM aspnet_Users
92
Pawel G.

Essaye ça

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

Vous déclareriez cette variable en dehors de votre boucle en tant que variable TSQL standard.

Je devrais également noter que c'est comme cela que vous feriez pour n'importe quel type de sélection dans une variable, pas seulement lorsqu'il s'agit de curseurs.

19
TheTXI

Pourquoi avez-vous besoin d'un curseur? Tout votre segment de code peut être remplacé par ceci, ce qui fonctionnera beaucoup plus rapidement sur un grand nombre de lignes.

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

Pour affecter une variable en toute sécurité, vous devez utiliser l'instruction SET-SELECT:

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

Assurez-vous d'avoir une parenthèse de début et de fin!

La raison pour laquelle la version de SET-SELECT est le moyen le plus sûr de définir une variable est double.

1. Le SELECT retourne plusieurs posts

Que se passe-t-il si les résultats de sélection suivants apparaissent dans plusieurs messages?

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

@PrimaryContactKey se verra attribuer la valeur du dernier message dans le résultat.

En réalité @PrimaryContactKey se verra attribuer une valeur par publication dans le résultat et contiendra donc la valeur de la dernière publication traitée par la commande SELECT.

La dernière publication est déterminée par les index clusterisés ou, si aucun index clusterisé n'est utilisé ou si la clé primaire n'est clusterisée, la "dernière" publication sera la dernière publication ajoutée. Dans le pire des cas, ce comportement pourrait être modifié chaque fois que l'indexation de la table est modifiée.

Avec une instruction SET-SELECT, votre variable sera définie sur null.

2. Le SELECT ne renvoie aucun message

Que se passe-t-il, lorsque vous utilisez la deuxième version du code, si votre sélection ne renvoie aucun résultat?

Contrairement à ce que vous pouvez croire, la valeur de la variable ne sera pas nulle - elle conservera sa valeur précédente !

En effet, comme indiqué ci-dessus, SQL affectera une valeur à la variable une fois par publication - ce qui signifie que la variable ne fera rien si le résultat ne contient aucune publication. Ainsi, la variable aura toujours la valeur qu'elle avait avant l'exécution de l'instruction.

Avec l'instruction SET-SELECT, la valeur sera null.

Voir aussi: SET versus SELECT lors de l'affectation de variables?

11
Erk