2009-04-30 43 views
63

Come faccio a memorizzare un valore di campo selezionato in una variabile da una query e utilizzarlo in un'istruzione di aggiornamento?Come assegnare un risultato selezionato a una variabile?

Ecco la mia procedura:

Sto scrivendo uno SQL Server 2005 T-SQL stored procedure che esegue le seguenti operazioni:

  1. ottiene elenco delle fatture id da tavolo fattura e negozi per Cursore
  2. Fetch fattura id dal cursore -> tmp_key variabile
  3. foreach tmp_key trova contatto con il cliente fattura primaria id dalla tabella cliente
  4. aggiorna il contatto con il cliente chiave con contatto principale id
  5. vicino cursore

Qui è il mio codice:

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 

Come faccio a memorizzare il PrimaryContactKey e utilizzarlo di nuovo nella clausola serie di l'istruzione di aggiornamento? Creo una variabile cursore o solo un'altra variabile locale con un tipo int?

+2

Come risposte @GilaMonster sotto, questa intera operazione può essere una singola istruzione UPDATE (una "operazione di set-based", da non confondere con un [un'istruzione SET T-SQL] (https : //msdn.microsoft.com/en-us/library/ms189484.aspx)) che è un approccio molto migliore (esecuzione più rapida, meno spese generali e molto meno codice). Sto solo sottolineando questo perché la domanda e tutte le attuali migliori risposte sono su come scrivere una dichiarazione SET, ma non è davvero l'approccio migliore per cominciare. – gregmac

risposta

40
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:
Questa domanda ha ottenuto molto più di trazione di quanto avrei anticipato. Si noti che non sto sostenendo l'uso del cursore nella mia risposta, ma piuttosto mostrando come assegnare il valore in base alla domanda.

18

Try This

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 

Si potrebbe dichiarare questa variabile al di fuori della vostra ciclo come solo una variabile TSQL standard.

Si noti anche che questo è come si farebbe per qualsiasi tipo di selezionare in una variabile, non solo quando si tratta di cursori.

13

Perché avete bisogno di un cursore a tutti? L'intero segmento di codice può essere sostituito da questo, che verrà eseguito molto più velocemente su un numero elevato di righe.

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

I cursori sono davvero malvagi? – phill

+4

Sono lenti. SQL Server è ottimizzato per query basate su set. È più veloce per operare su un milione di righe in una query piuttosto che operare su una riga un milione di volte. Aggiungete a ciò il sovraccarico dei cursori e chiedete maggiori problemi di prestazioni usando i cursori anziché le operazioni basate sui set Testare la soluzione del cursore e la mia query, vedere quali sono i tempi di esecuzione dei due. – GilaMonster

+0

Grazie amico !!!! Questo ha fatto per me, anche più semplice di come modificare il cursore per il mio scenario specifico. Sei un campione! –

81

Ho appena avuto lo stesso problema e ...

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

o ancora più breve:

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

Haha, mi piace questo. dovrebbe essere molto semplice per l'assegnazione del valore scalare. I cursori odio bla3 .. Fortunatamente googling trovato questa piccola risposta. – CallMeLaNN

+3

Non so perché 'set @userId = (seleziona il primo 1 UserId di aspnet_Users)' ** senza parentesi ** porterà a "sintassi errata vicino alla selezione"! – CallMeLaNN

+0

Questo forum mostra l'approccio corretto per la parte superiore: http://www.sqlservercentral.com/Forums/Topic496124-169-1.aspx –

9

Al fine di assegnare una variabile in modo sicuro è necessario utilizzare il SET-SELECT dichiarazione:

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

Assicurarsi di avere sia una parentesi iniziale che una finale!

La ragione per la versione SET-SELECT è il modo più sicuro per impostare una variabile è duplice.

1.SELECT restituisce diversi post

Cosa succede se la selezione seguente risulta in più post?

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

@PrimaryContactKey sarà assegnato il valore dall'ultimo messaggio nel risultato.

In effetti @PrimaryContactKey verrà assegnato un valore per post nel risultato, quindi conterrà il valore dell'ultimo post che il comando SELECT stava elaborando.

Quale post è "last" è determinato da qualsiasi indice cluster o, se non viene utilizzato alcun indice cluster o la chiave primaria è in cluster, il post "ultimo" sarà il post aggiunto più di recente. Questo comportamento potrebbe, nel peggiore dei casi, essere modificato ogni volta che viene modificata l'indicizzazione della tabella.

Con una istruzione SET-SELECT la variabile sarà impostata su null.

2. Il SELECT restituisce messaggi

Cosa succede, quando si utilizza la seconda versione del codice, se la vostra selezione non restituisce un risultato a tutti?

In un contrariamente a quello che si può credere il valore della variabile non sarà nulla - manterrà il suo valore precedente!

Questo è perché, come detto in precedenza, SQL assegnare un valore alla variabile una volta per ogni post - il che significa che non farà nulla con la variabile se il risultato non contiene messaggi. Quindi, la variabile avrà ancora il valore che aveva prima di eseguire l'istruzione.

Con l'istruzione SET-SELECT il valore sarà null.

Consulta anche: SET versus SELECT when assigning variables?

+0

Erk è corretto e avrebbe dovuto essere contrassegnato come risposta. Il secondo punto è quello che mi ha catturato di recente ... – wexman

+0

@wexman: perché, grazie! Ho avuto anche il mio run-in con # 2 ... – Erk

+0

Ho appena risolto un bug con il codice che segue il caso 1 sopra. Sembrava che il programmatore originale non si preoccupasse del fatto che diverse righe fossero selezionate, ma sfortunatamente un MS SQL Server può talvolta restituire le righe in un altro ordine rispetto a quello che erano stati inseriti per nessun'altra ragione apparente dell'ottimizzazione ... il risultato era casuale e confuso ... – Erk

Problemi correlati