web-dev-qa-db-fra.com

SET versus SELECT lors de l'affectation de variables?

Quelles sont les différences entre les instructions SET et SELECT lors de l'affectation de variables dans T-SQL?

268
juur

Citation , qui résume de cet article :

  1. SET est la norme ANSI pour l’affectation des variables, pas SELECT.
  2. SET ne peut affecter qu'une variable à la fois, SELECT peut effectuer plusieurs affectations à la fois.
  3. En cas d’affectation à partir d’une requête, SET ne peut affecter qu’une valeur scalaire. Si la requête renvoie plusieurs valeurs/lignes, SET émettra une erreur. SELECT affectera l'une des valeurs à la variable et masquera le fait que plusieurs valeurs ont été renvoyées (vous ne saurez donc probablement jamais pourquoi quelque chose ne va pas ailleurs - amusez-vous à résoudre ce problème)
  4. Lors de l'affectation à partir d'une requête, si aucune valeur n'est renvoyée, SET affectera NULL, alors que SELECT n'effectuera pas l'affectation du tout (de sorte que la variable ne sera pas modifiée par rapport à sa valeur précédente).
  5. En ce qui concerne les différences de vitesse, il n'y a pas de différence directe entre SET et SELECT. Cependant, la capacité de SELECT à effectuer plusieurs affectations en une seule fois lui confère un léger avantage en termes de vitesse par rapport à SET.
393
OMG Ponies

Je crois que SET est la norme ANSI alors que SELECT ne l’est pas. Notez également le comportement différent de SET et SELECT dans l'exemple ci-dessous lorsqu'une valeur est introuvable.

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' */
144
Joe Stefanelli

Lors de la rédaction des requêtes, gardez à l'esprit cette différence:

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*/
24
GorkemHalulu

En plus de l'ANSI et de la vitesse, etc., il y a une différence très importante qui compte toujours pour moi; plus que la norme ANSI et la vitesse. Le nombre de bugs que j'ai corrigés en raison de cet important oubli est important. Je cherche cela tout le temps pendant les revues de code.

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

Presque toujours, ce n'est pas ce que le développeur a l'intention de faire. Dans ce qui précède, la requête est simple, mais j’ai vu des requêtes assez complexes et déterminer si elle renverrait une valeur unique ou non n’est pas triviale. La requête est souvent plus complexe que cela et, par hasard, elle renvoie une valeur unique. Pendant le test du développeur, tout va bien. Mais cela ressemble à une bombe à retardement et posera des problèmes lorsque la requête renvoie plusieurs résultats. Pourquoi? Parce qu'il va simplement affecter la dernière valeur à la variable.

Essayons maintenant la même chose avec SET:

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

Vous recevrez une erreur:

La sous-requête a renvoyé plus d'une valeur. Ceci n'est pas autorisé lorsque la sous-requête suit =,! =, <, <=,>,> = Ou lorsque la sous-requête est utilisée comme expression.

C'est étonnant et très important car pourquoi voudriez-vous attribuer un "dernier élément du résultat" trivial au @employeeId. Avec select vous n'obtiendrez jamais d'erreur et vous passerez des minutes, des heures au débogage.

Peut-être recherchez-vous un seul identifiant et SET vous obligera à corriger votre requête. Ainsi, vous pouvez faire quelque chose comme:

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

Nettoyer

drop table Employee;

En conclusion, utilisez:

  • SET: Lorsque vous souhaitez attribuer une valeur unique à une variable et que votre variable est pour une valeur unique.
  • SELECT: Lorsque vous souhaitez affecter plusieurs valeurs à une variable. La variable peut être une table, une table temporaire, une variable de table, etc.
3
CodingYoshi