SET versus SELECT lors de l'attribution des variables?

Réponses:

411

Citation , qui résume cet article :

  1. SET est la norme ANSI pour l'attribution des variables, SELECT ne l'est pas.
  2. SET ne peut affecter qu'une seule 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 génère 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 retournée, SET affectera NULL, où SELECT ne fera pas du tout l'affectation (donc 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érences directes entre SET et SELECT. Cependant, la capacité de SELECT à effectuer plusieurs affectations en une seule fois lui confère un léger avantage sur la vitesse par rapport à SET.
Poneys OMG
la source
3
Je n'ai pas downvote, mais ce qui suit n'est pas tout à fait correct: "En ce qui concerne les différences de vitesse - il n'y a pas de différences directes entre SET et SELECT". Si vous attribuez plusieurs valeurs en une seule sélection, cela peut être beaucoup plus rapide que via plusieurs ensembles. Google up "L'affectation de plusieurs variables avec un SELECT fonctionne plus rapidement"
AK
14
@AlexKuznetsov: La phrase suivante dit exactement cela.
OMG Ponies
3
@OMG Ponies: Cela peut être 10 fois plus rapide ou plus, donc je ne sais pas si c'est "un léger avantage de vitesse".
AK
2
Surtout lors de l'utilisation d'une boucle While, j'ai constaté d'énormes gains de performances en définissant / réinitialisant toutes mes variables à l'aide d'une sélection par rapport à plusieurs ensembles. Je peux également consolider ma logique variable dans une sélection pour que tous s'exécutent en même temps: Exemple: SELECT @Int = @Int + 1, @Int = @Int + 1s'il est @Intdémarré à 0, il se termine alors à 2. Cela peut être très utile lors de manipulations de chaînes successives.
MikeTeeVee
Discussion intéressante sur la différence de performance. Si la définition de plusieurs valeurs via select est plus rapide (pour coder et exécuter), un moyen sûr pour éviter le point 4 (la valeur de votre variable ne changeant pas si la requête renvoie null) consiste à définir explicitement votre variable sur null avant la sélection. Une fois que vous avez pris cela en compte, comment les deux se comparent-ils pour les performances? (Remarque: je ne comprends pas la raison pour laquelle vous ne sélectionnez pas de valeur nulle pour votre variable dans le cas où une requête renvoie la valeur null. Quand voudriez-vous cela?)
youcantryreachingme
155

Je crois que SETc'est la norme ANSI alors que ce SELECTn'est pas le cas. Notez également le comportement différent de SETvs SELECTdans l'exemple ci-dessous lorsqu'une valeur n'est pas trouvée.

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
la source
4
+1 Il vaut mieux courir une fois afin de comprendre, vérifier, jouer, mémoriser cela pour simplement le lire mais les autres réponses ne sont que du texte
Gennady Vanin Геннадий Ванин
4
Si vous l'avez utilisé, select @var = (select name from master.sys.tables where name = 'qwerty')vous obtiendrez @var comme nul. L'exemple que vous donnez n'est pas la même requête.
Zack
4
Vous avez (select name from master.sys.tables where name = 'qwerty')pour l'un et name from master.sys.tables where name = 'qwerty'pour l'autre ... vous ne voyez pas ça?
Zack
4
@Zack: Chacun est la syntaxe correcte pour ce que j'essaie de faire une démonstration; la différence entre l'utilisation de SET et SELECT pour attribuer une valeur à une variable lorsque la requête sous-jacente ne renvoie aucun résultat.
Joe Stefanelli
5
(select name from master.sys.tables where name = 'qwerty')est une sous-requête scalaire et name from master.sys.tables where name = 'qwerty'est une requête simple. Les deux expressions différentes ne sont pas censées produire les mêmes résultats, bien qu'il semble que vous sous-entendiez qu'elles le devraient. Si vous essayez de dire que les mots clés SETet SELECTont des implémentations différentes, vous ne devez pas utiliser deux expressions différentes dans vos exemples. msdn.microsoft.com/en-us/library/ms187330.aspx
Zack
27

Lors de l'écriture de requêtes, cette différence doit être gardée à l'esprit:

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
la source
très agréable, succinct
SimplyInk
8

Mis à part celui qui est ANSI et la vitesse, etc., il y a une différence très importante qui compte toujours pour moi; plus que ANSI et la vitesse. Le nombre de bugs que j'ai corrigés en raison de cette importante négligence est important. Je recherche cela pendant les révisions de code tout le temps.

-- 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 l'intention du développeur. Dans ce qui précède, la requête est simple mais j'ai vu des requêtes qui sont assez complexes et déterminer si elle retournera une valeur unique ou non, n'est pas trivial. La requête est souvent plus complexe que cela et, par hasard, elle a renvoyé une seule valeur. Pendant les tests des développeurs, tout va bien. Mais cela ressemble à une bombe à retardement et causera des problèmes lorsque la requête renvoie plusieurs résultats. Pourquoi? Parce qu'il affectera simplement 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. Cela 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 de résultat" trivial au @employeeId. Avec selectvous, vous n'obtiendrez jamais d'erreur et vous passerez des minutes, des heures à déboguer.

Peut-être que vous recherchez un seul identifiant et SETvous obligerez à 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 affecter une seule valeur à une variable et que votre variable est pour une seule valeur.
  • SELECT: Lorsque vous souhaitez affecter plusieurs valeurs à une variable. La variable peut être une table, une table temporaire ou une variable de table, etc.
CodageYoshi
la source