Ответы:
Цитата , которая резюмирует из этой статьи :
- SET - это стандарт ANSI для назначения переменных, а SELECT - нет.
- SET может назначать только одну переменную одновременно, SELECT может выполнять несколько назначений одновременно.
- При назначении из запроса SET может назначать только скалярное значение. Если запрос возвращает несколько значений / строк, тогда SET вызовет ошибку. SELECT назначит одно из значений переменной и скроет тот факт, что было возвращено несколько значений (так что вы, вероятно, никогда не узнаете, почему что-то пошло не так в другом месте - получайте удовольствие от устранения этой проблемы)
- При присваивании из запроса, если не возвращено значение, SET будет присваивать NULL, где SELECT вообще не будет выполнять присваивание (поэтому переменная не будет изменена по сравнению с ее предыдущим значением)
- Что касается различий в скорости - нет прямой разницы между SET и SELECT. Однако способность SELECT выполнять несколько заданий за один выстрел дает ему небольшое преимущество в скорости перед SET.
SELECT @Int = @Int + 1, @Int = @Int + 1
если он @Int
начинается с 0, он заканчивается как 2. Это может быть очень полезно при последовательных манипуляциях со строками.
Я считаю, что SET
это стандарт ANSI, тогда как SELECT
нет. Также обратите внимание на различное поведение SET
vs. 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' */
select @var = (select name from master.sys.tables where name = 'qwerty')
вы бы получили @var как ноль. Пример, который вы приводите, - это не тот же запрос.
(select name from master.sys.tables where name = 'qwerty')
для одного, а name from master.sys.tables where name = 'qwerty'
для другого ... разве вы этого не видите?
(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
При написании запросов следует учитывать эту разницу:
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*/
Помимо 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
: Когда вы хотите назначить несколько значений переменной. Переменная может быть таблицей, временной таблицей или табличной переменной и т. Д.