SET против SELECT при назначении переменных?


Ответы:


412

Цитата , которая резюмирует из этой статьи :

  1. SET - это стандарт ANSI для назначения переменных, а SELECT - нет.
  2. SET может назначать только одну переменную одновременно, SELECT может выполнять несколько назначений одновременно.
  3. При назначении из запроса SET может назначать только скалярное значение. Если запрос возвращает несколько значений / строк, тогда SET вызовет ошибку. SELECT назначит одно из значений переменной и скроет тот факт, что было возвращено несколько значений (так что вы, вероятно, никогда не узнаете, почему что-то пошло не так в другом месте - получайте удовольствие от устранения этой проблемы)
  4. При присваивании из запроса, если не возвращено значение, SET будет присваивать NULL, где SELECT вообще не будет выполнять присваивание (поэтому переменная не будет изменена по сравнению с ее предыдущим значением)
  5. Что касается различий в скорости - нет прямой разницы между SET и SELECT. Однако способность SELECT выполнять несколько заданий за один выстрел дает ему небольшое преимущество в скорости перед SET.

3
Я не понизил голос, но следующее не совсем правильно: «Что касается различий в скорости - нет прямой разницы между SET и SELECT». Если вы назначаете несколько значений на одном участке, это может быть намного быстрее, чем с помощью множества наборов. Google up «Назначение нескольких переменных одним SELECT работает быстрее»
AK

14
@AlexKuznetsov: В последующем предложении говорится именно это.
OMG Ponies

3
@OMG Ponies: это может быть в 10 раз быстрее или больше, поэтому я не уверен, что это «небольшое преимущество в скорости».
AK

2
Особенно при использовании цикла While-Loop я наблюдал ОГРОМНОЕ повышение производительности, устанавливая / реинициализируя все мои переменные, используя один выбор против множества наборов. Я также могу объединить свою переменную-логику в Select, чтобы все тоже запускались одновременно: Пример: SELECT @Int = @Int + 1, @Int = @Int + 1если он @Intначинается с 0, он заканчивается как 2. Это может быть очень полезно при последовательных манипуляциях со строками.
MikeTeeVee

Интересная дискуссия о разнице в производительности. Если установка нескольких значений с помощью выбора происходит быстрее (для кодирования и выполнения), то один отказоустойчивый способ избежать точки 4 (значение переменной не может измениться, если запрос возвращает ноль) состоит в том, чтобы явно установить для переменной значение ноль до выбора. Как только вы учитываете это, как они сравниваются по производительности? (Примечание: я не понимаю обоснования для выбора, не устанавливающего вашу переменную в нуль, если запрос возвращает ноль. Когда вы когда-нибудь захотите этого?)
youcantryreachingme

155

Я считаю, что SETэто стандарт ANSI, тогда как SELECTнет. Также обратите внимание на различное поведение SETvs. SELECTв приведенном ниже примере, когда значение не найдено.

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' */

4
+1 Лучше бегать один раз, чтобы понять, проверить, поиграть, запомнить, чтобы просто прочитать, а другие ответы - просто текст
Геннадий Ванин Геннадий Ванин

4
Если бы вы на самом деле использовали, select @var = (select name from master.sys.tables where name = 'qwerty')вы бы получили @var как ноль. Пример, который вы приводите, - это не тот же запрос.
Зак

4
Вы (select name from master.sys.tables where name = 'qwerty')для одного, а name from master.sys.tables where name = 'qwerty'для другого ... разве вы этого не видите?
Зак

4
@Zack: каждый из них является правильным синтаксисом для того, что я пытаюсь продемонстрировать; разница между использованием SET и SELECT для присвоения значения переменной, когда базовый запрос не возвращает результатов.
Джо Стефанелли

5
(select name from master.sys.tables where name = 'qwerty')это скалярный подзапрос, и name from master.sys.tables where name = 'qwerty'это простой запрос. Предполагается, что два разных выражения не дают одинаковых результатов, хотя, похоже, вы намекаете на это. Если вы пытаетесь сказать , что SETи SELECTключевые слова имеют различные варианты реализации, вы не должны использовать два различных выражения в ваших примерах. msdn.microsoft.com/en-us/library/ms187330.aspx
Зак

27

При написании запросов следует учитывать эту разницу:

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

очень мило, лаконично
SimplyInk

8

Помимо ANSI, скорости и т. Д., Есть очень важное различие, которое всегда важно для меня; больше, чем ANSI и скорость. Количество ошибок, которые я исправил из-за этого важного упущения, велико. Я ищу это во время проверки кода все время.

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

Почти всегда, это не то, что разработчик намеревается. В приведенном выше запрос прост, но я видел запросы, которые являются довольно сложными и выяснить, будет ли он возвращать одно значение или нет, не тривиально. Запрос часто является более сложным, чем этот, и случайно он возвращает одно значение. Во время тестирования разработчиков все в порядке. Но это похоже на тикающую бомбу и вызовет проблемы, когда запрос выдаст несколько результатов. Зачем? Потому что он просто назначит последнее значение переменной.

Теперь давайте попробуем то же самое с SET:

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

Вы получите ошибку:

Подзапрос вернул более 1 значения. Это недопустимо, если подзапрос следует =,! =, <, <=,>,> = Или когда подзапрос используется в качестве выражения.

Это удивительно и очень важно, потому что зачем вам назначать какой-то тривиальный «последний элемент в результате» @employeeId. С участиемselect вами никогда не возникнет никакой ошибки и вы потратите минуты, часы на отладку.

Возможно, вы ищете один идентификатор и SETзаставите вас исправить ваш запрос. Таким образом, вы можете сделать что-то вроде:

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

уборка

drop table Employee;

В заключение используйте:

  • SET: Когда вы хотите присвоить одно значение переменной, и ваша переменная предназначена для одного значения.
  • SELECT: Когда вы хотите назначить несколько значений переменной. Переменная может быть таблицей, временной таблицей или табличной переменной и т. Д.
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.