¿SET versus SELECT al asignar variables?

Respuestas:

411

Cita , que resume de este artículo :

  1. SET es el estándar ANSI para la asignación de variables, SELECT no lo es.
  2. SET solo puede asignar una variable a la vez, SELECT puede realizar múltiples asignaciones a la vez.
  3. Si asigna desde una consulta, SET solo puede asignar un valor escalar. Si la consulta devuelve múltiples valores / filas, SET generará un error. SELECT asignará uno de los valores a la variable y ocultará el hecho de que se devolvieron múltiples valores (por lo que es probable que nunca sepas por qué algo iba mal en otro lugar; diviértete resolviendo ese problema)
  4. Al asignar desde una consulta si no se devuelve ningún valor, SET asignará NULL, donde SELECT no realizará la asignación (por lo que la variable no cambiará de su valor anterior)
  5. En cuanto a las diferencias de velocidad, no hay diferencias directas entre SET y SELECT. Sin embargo, la capacidad de SELECT para realizar múltiples tareas en un solo disparo le da una ligera ventaja de velocidad sobre SET.
Ponis OMG
fuente
3
No voté en contra, pero lo siguiente no es del todo correcto: "En cuanto a las diferencias de velocidad, no hay diferencias directas entre SET y SELECT". Si asigna múltiples valores en una selección, eso puede ser mucho más rápido que a través de conjuntos múltiples. Google up "Asignar múltiples variables con un SELECT funciona más rápido"
AK
14
@AlexKuznetsov: La oración posterior dice exactamente eso.
OMG Ponies
3
@OMG Ponis: puede ser 10 veces más rápido o más, por lo que no estoy seguro de si es una "ligera ventaja de velocidad".
AK
2
Especialmente cuando uso un While-Loop, he visto enormes ganancias de rendimiento al configurar / reinicializar todas mis variables usando one-Select vs. many-Set's. También puedo consolidar mi Lógica Variable en una Selección para ejecutar todo a la vez: Ejemplo: SELECT @Int = @Int + 1, @Int = @Int + 1si se @Intinicia como 0, termina como 2. Esto puede ser muy útil cuando se realizan sucesivas manipulaciones de cadenas.
MikeTeeVee
Interesante discusión sobre la diferencia de rendimiento. Si establecer valores múltiples a través de select es más rápido (codificar y ejecutar), entonces una forma segura de evitar el punto 4 (su valor de variable no cambia si la consulta devuelve nulo) es establecer explícitamente su variable en nulo antes de la selección. Una vez que tiene en cuenta eso, ¿cómo se comparan los dos para el rendimiento? (Nota al margen: no entiendo la justificación para seleccionar no establecer su variable en nulo en caso de que una consulta devuelva nulo. ¿Cuándo querría eso?)
youcantryreachingme
155

Creo que SETes el estándar ANSI, mientras que el SELECTno lo es. Observe también el comportamiento diferente de SETvs. SELECTen el siguiente ejemplo cuando no se encuentra un valor.

declare @var varchar(20)
set @var = 'Joe'
set @var = (select name from master.sys.tables where name = 'qwerty')
select @var /* @var is now NULL */

set @var = 'Joe'
select @var = name from master.sys.tables where name = 'qwerty'
select @var /* @var is still equal to 'Joe' */
Joe Stefanelli
fuente
44
+1 Es mejor correr una vez para comprender, verificar, jugar, memorizar eso para leer, pero otras respuestas son solo texto
Gennady Vanin Геннадий Ванин
44
Si realmente select @var = (select name from master.sys.tables where name = 'qwerty')lo usaras, obtendrías @var como nulo. El ejemplo que está dando no es la misma consulta.
Zack
44
Tienes (select name from master.sys.tables where name = 'qwerty')para uno, y name from master.sys.tables where name = 'qwerty'para el otro ... ¿no lo ves?
Zack
44
@Zack: cada una es la sintaxis correcta de lo que estoy intentando demostrar; La diferencia entre usar SET vs. SELECT para asignar un valor a una variable cuando la consulta subyacente no devuelve ningún resultado.
Joe Stefanelli
55
(select name from master.sys.tables where name = 'qwerty')es una subconsulta escalar y name from master.sys.tables where name = 'qwerty'es una consulta simple. No se supone que las dos expresiones diferentes produzcan los mismos resultados, aunque parece que estás insinuando que deberían hacerlo. Si está tratando de decir que las palabras clave SETy SELECTtienen implementaciones diferentes, no debe usar dos expresiones diferentes en sus ejemplos. msdn.microsoft.com/en-us/library/ms187330.aspx
Zack
27

Al escribir consultas, esta diferencia debe tenerse en cuenta:

DECLARE @A INT = 2

SELECT  @A = TBL.A
FROM    ( SELECT 1 A ) TBL
WHERE   1 = 2

SELECT  @A
/* @A is 2*/

---------------------------------------------------------------

DECLARE @A INT = 2

SET @A = ( 
            SELECT  TBL.A
            FROM    ( SELECT 1 A) TBL
            WHERE   1 = 2
         )

SELECT  @A
/* @A is null*/
GorkemHalulu
fuente
muy agradable, sucinto
SimplyInk
8

Además de ser ANSI y velocidad, etc., hay una diferencia muy importante que siempre me importa; más que ANSI y velocidad. El número de errores que he solucionado debido a este importante pasar por alto es grande. Busco esto durante las revisiones de código todo el tiempo.

-- Arrange
create table Employee (EmployeeId int);
insert into dbo.Employee values (1);
insert into dbo.Employee values (2);
insert into dbo.Employee values (3);

-- Act
declare @employeeId int;
select @employeeId = e.EmployeeId from dbo.Employee e;

-- Assert
-- This will print 3, the last EmployeeId from the query (an arbitrary value)
-- Almost always, this is not what the developer was intending. 
print @employeeId; 

Casi siempre, eso no es lo que el desarrollador pretende. En lo anterior, la consulta es sencilla, pero he visto consultas que son bastante complejas y averiguar si devolverá un solo valor o no, no es trivial. La consulta suele ser más compleja que esta y, por casualidad, ha devuelto un valor único. Durante las pruebas de desarrollador, todo está bien. Pero esto es como una bomba de relojería y causará problemas cuando la consulta devuelva múltiples resultados. ¿Por qué? Porque simplemente asignará el último valor a la variable.

Ahora intentemos lo mismo con SET :

 -- Act
 set @employeeId = (select e.EmployeeId from dbo.Employee e);

Recibirá un error:

La subconsulta devolvió más de 1 valor. Esto no está permitido cuando la subconsulta sigue =,! =, <, <=,>,> = O cuando la subconsulta se usa como una expresión.

Eso es sorprendente y muy importante porque ¿por qué querrías asignarle un "último elemento en resultado" trivial al @employeeId . Con selectusted nunca obtendrá ningún error y pasará minutos, horas depurando.

Tal vez, está buscando un único ID y SETlo obligará a corregir su consulta. Por lo tanto, puede hacer algo como:

-- Act
-- Notice the where clause
set @employeeId = (select e.EmployeeId from dbo.Employee e where e.EmployeeId = 1);
print @employeeId;

Limpiar

drop table Employee;

En conclusión, use:

  • SET: Cuando desea asignar un solo valor a una variable y su variable es para un solo valor.
  • SELECT: Cuando desea asignar varios valores a una variable. La variable puede ser una tabla, una tabla temporal o una variable de tabla, etc.
CodificaciónYoshi
fuente