SET versus SELECT ao atribuir variáveis?

Respostas:

411

Citação , que resume este artigo :

  1. SET é o padrão ANSI para atribuição de variáveis, SELECT não é.
  2. O SET pode atribuir apenas uma variável por vez, o SELECT pode fazer várias atribuições ao mesmo tempo.
  3. Ao atribuir a partir de uma consulta, o SET só pode atribuir um valor escalar. Se a consulta retornar vários valores / linhas, o SET gerará um erro. SELECT atribuirá um dos valores à variável e ocultará o fato de que vários valores foram retornados (portanto, você provavelmente nunca saberia por que algo estava errado em outro lugar - divirta-se solucionando problemas com esse)
  4. Ao atribuir a partir de uma consulta se não houver valor retornado, o SET atribuirá NULL, em que SELECT não fará a atribuição de todo (portanto, a variável não será alterada em relação ao valor anterior)
  5. Quanto às diferenças de velocidade - não há diferenças diretas entre SET e SELECT. No entanto, a capacidade do SELECT de fazer várias atribuições de uma só vez oferece uma pequena vantagem de velocidade em relação ao SET.
Pôneis OMG
fonte
3
Eu não reduzi o voto, mas o seguinte não está muito correto: "No que diz respeito às diferenças de velocidade - não há diferenças diretas entre SET e SELECT". Se você atribuir vários valores em uma barra, isso pode ser muito mais rápido que através de vários conjuntos. Google up "Atribuir várias variáveis ​​com um SELECT funciona mais rápido"
AK
14
@AlexKuznetsov: A frase depois diz exatamente isso.
OMG Ponies
3
Pôneis @ OMG: Pode ser 10 vezes mais rápido ou mais, então não tenho certeza se é "uma ligeira vantagem de velocidade".
AK
2
Especialmente ao usar um loop while, eu tenho visto ganhos de desempenho ENORME definindo / reinicializando todas as minhas variáveis ​​usando um conjunto e vários conjuntos. Também posso consolidar minha Variable-Logic em um Select para executar todos de uma vez também: Exemplo: SELECT @Int = @Int + 1, @Int = @Int + 1se @Intiniciado como 0, termina como 2. Isso pode ser muito útil ao fazer manipulações sucessivas de strings.
MikeTeeVee
Discussão interessante sobre diferença de desempenho. Se a configuração de vários valores via select for mais rápida (codificar e executar), uma maneira segura de evitar o ponto 4 (seu valor da variável não será alterado se a consulta retornar nula) é definir explicitamente sua variável como null antes da seleção. Depois de considerar isso, como os dois se comparam ao desempenho? (Observação: não entendo a justificativa para selecionar não definir sua variável como nula no caso de uma consulta retornar nula. Quando você desejaria isso?)
youcantryreachingme
155

Eu acredito que SETé o padrão ANSI, enquanto o SELECTnão é. Observe também o comportamento diferente de SETvs. SELECTno exemplo abaixo quando um valor não for encontrado.

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
fonte
4
+1 É melhor executar uma vez para entender, checar, reproduzir e memorizar que basta ler, mas outras respostas são apenas texto
Gennady Vanin Геннадий Ванин
4
Se você realmente usasse select @var = (select name from master.sys.tables where name = 'qwerty'), obteria @var como nulo. O exemplo que você está dando não é a mesma consulta.
Zack
4
Você tem (select name from master.sys.tables where name = 'qwerty')um e name from master.sys.tables where name = 'qwerty'o outro ... você não vê isso?
Zack
4
@Zack: Cada uma é a sintaxe correta para o que estou tentando demonstrar; a diferença entre usar SET x SELECT para atribuir um valor a uma variável quando a consulta subjacente não retornar resultados.
Joe Stefanelli
5
(select name from master.sys.tables where name = 'qwerty')é uma subconsulta escalar e name from master.sys.tables where name = 'qwerty'é uma consulta simples. As duas expressões diferentes não devem produzir os mesmos resultados, embora pareça que você está sugerindo que elas deveriam. Se você está tentando dizer que as palavras SET- SELECTchave e têm implementações diferentes, não deve usar duas expressões diferentes nos seus exemplos. msdn.microsoft.com/en-us/library/ms187330.aspx
Zack
27

Ao escrever consultas, essa diferença deve ser lembrada:

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
fonte
muito bom, sucinto
SimplyInk 26/02
8

Além de ANSI e velocidade, etc., há uma diferença muito importante que sempre importa para mim; mais do que ANSI e velocidade. O número de bugs que eu consertei devido a essa negligência importante é grande. Eu procuro isso durante revisões de código o tempo todo.

-- 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; 

Quase sempre, não é isso que o desenvolvedor pretende. No exposto, a consulta é direta, mas já vi consultas bastante complexas e descobrir se retornará um valor único ou não, não é trivial. A consulta geralmente é mais complexa que essa e, por acaso, está retornando valor único. Durante o teste do desenvolvedor, tudo está bem. Mas isso é como uma bomba-relógio e causará problemas quando a consulta retornar vários resultados. Por quê? Porque simplesmente atribui o último valor à variável.

Agora vamos tentar a mesma coisa com SET:

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

Você receberá um erro:

Subconsulta retornou mais de 1 valor. Isso não é permitido quando a subconsulta segue =,! =, <, <=,>,> = Ou quando a subconsulta é usada como uma expressão.

Isso é incrível e muito importante, porque por que você deseja atribuir algum "último item no resultado" trivial ao arquivo @employeeId. Com selectvocê nunca receberá nenhum erro e você passará minutos, horas depurando.

Talvez você esteja procurando um único ID e SETforçará você a corrigir sua consulta. Assim, você pode fazer algo como:

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

Limpar

drop table Employee;

Em conclusão, use:

  • SET: Quando você deseja atribuir um único valor a uma variável e sua variável é para um único valor.
  • SELECT: Quando você deseja atribuir vários valores a uma variável. A variável pode ser uma tabela, tabela temporária ou variável de tabela etc.
CodificaçãoYoshi
fonte