2009-04-30 26 views
63

¿Cómo almaceno un valor de campo seleccionado en una variable de una consulta y lo uso en una declaración de actualización?¿Cómo asignar un resultado de selección a una variable?

Aquí es mi procedimiento:

Estoy escribiendo un SQL Server 2005 procedimiento almacenado de T-SQL, que hace lo siguiente:

  1. consigue lista de facturas de identificación de de la tabla de facturas y tiendas para Cursor
  2. Fetch Identificación factura desde el cursor -> tmp_key variables
  3. foreach tmp_key encuentra contacto principal cliente de la factura Identificación de tabla de clientes
  4. actualiza el contacto con el cliente llave con contacto primario Identificación
  5. cerca del cursor

Aquí está mi 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 

¿Cómo almacenar la PrimaryContactKey y usarlo de nuevo en la cláusula conjunto de la siguiente instrucción de actualización? ¿Creo una variable de cursor o simplemente otra variable local con un tipo int?

+2

Como respuestas @GilaMonster abajo, toda esta operación puede ser una declaración única actualización (una "operación de configuración basada", que no debe confundirse con una [sentencia SET T-SQL] (https : //msdn.microsoft.com/en-us/library/ms189484.aspx)) que es un enfoque mucho mejor (ejecución más rápida, menos sobrecarga y significativamente menos código). Solo estoy señalando esto porque la pregunta y todas las respuestas principales actuales son sobre cómo escribir una declaración SET, pero en realidad no es el mejor enfoque para empezar. – gregmac

Respuesta

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:
Esta pregunta se ha vuelto mucho más tracción de lo que hubiera esperado. Tenga en cuenta que no defiendo el uso del cursor en mi respuesta, sino que muestra cómo asignar el valor en función de la pregunta.

18

Trate Esta

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 

Usted podría declarar esta variable fuera de su bucle como se acaba de una variable TSQL estándar.

También debería tener en cuenta que así es como lo haría para cualquier tipo de selección en una variable, no solo cuando se trata de cursores.

13

¿Por qué necesita un cursor en absoluto? Su segmento completo de código puede ser reemplazado por este, que se ejecutará mucho más rápido en grandes cantidades de filas.

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

¿Los cursores realmente están mal visto? – phill

+4

Son lentos. SQL Server está optimizado para consultas basadas en conjunto. Es más rápido para que opere en un millón de filas en una consulta que para operar en una fila un millón de veces. Agregue a eso la sobrecarga que tienen los cursores, y está solicitando problemas de rendimiento importantes mediante el uso de cursores en lugar de operaciones basadas en conjuntos Pruebe su solución de cursor y mi consulta, vea cuáles son los tiempos de ejecución de los dos. – GilaMonster

+0

Gracias hombre !!!! Esto lo hizo por mí, también es más fácil que resolver cómo cambiar el cursor para mi escenario específico. ¡Eres un campeon! –

81

yo sólo tenía el mismo problema y ...

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

o incluso más corto:

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

Jaja, me gusta esto. debería ser muy simple para asignar un valor escalar. Odio los cursores bla3 ... Suerte Google encontró esta pequeña respuesta. – CallMeLaNN

+3

No lo sé porque 'set @userId = (seleccione 1 UserId superior de aspnet_Users)' ** sin paréntesis ** dará lugar a "sintaxis incorrecta cerca de seleccionar"! – CallMeLaNN

+0

Este foro muestra el enfoque adecuado para la parte superior: http://www.sqlservercentral.com/Forums/Topic496124-169-1.aspx –

9

Con el fin de asignar una variable con seguridad que tiene que utilizar el SET-SELECT declaración:

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

Asegúrese de que ha ¡ve un paréntesis inicial y uno final!

El motivo por el cual la versión SET-SELECT es la forma más segura de establecer una variable es doble.

1.El SELECT devuelve varios mensajes

¿Qué ocurre si los siguientes resultados de selección en varias publicaciones?

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

@PrimaryContactKey se le asignará el valor del último mensaje en el resultado.

De hecho, a @PrimaryContactKey se le asignará un valor por publicación en el resultado, por lo tanto, contendrá el valor de la última publicación que procesó el comando SELECT.

Qué publicación es "última" está determinada por cualquier índice agrupado o, si no se usa un índice agrupado o la clave principal está agrupada, la "última" publicación será la publicación agregada más recientemente. Este comportamiento podría, en el peor de los casos, alterarse cada vez que se cambie la indexación de la tabla.

Con una instrucción SET-SELECT su variable se establecerá en null.

2. El SELECT no devolvió los mensajes

¿Qué sucede, cuando se utiliza la segunda versión del código, si su selecto no devuelve un resultado en absoluto?

De forma contraria a lo que pueda creer, el valor de la variable no será nulo - retendrá ¡es un valor anterior!

Esto es así porque, como se ha dicho, SQL asignará un valor a la variable una vez por mensaje - lo que significa que no va a hacer nada con la variable si el resultado no contiene mensajes. Por lo tanto, la variable seguirá teniendo el valor que tenía antes de ejecutar la declaración.

Con la instrucción SET-SELECT el valor será null.

Consulte también: SET versus SELECT when assigning variables?

+0

Erk es correcto y debería haber sido marcado como respuesta. El segundo punto es lo que me atrapó recientemente ... – wexman

+0

@wexman: ¡gracias! He tenido mis encontronazos con el # 2 también ... – Erk

+0

Acabo de resolver un error con el código siguiente al caso 1 anterior. Al programador original parecía no importarle que se seleccionaran varias filas, pero desafortunadamente un servidor MS SQL a veces puede devolver las filas en otro orden que el que se insertaron sin otro motivo aparente que la optimización ... el resultado fue aleatorio y confuso ... – Erk

Cuestiones relacionadas