web-dev-qa-db-ja.com

変数割り当て時のSETとSELECTの違い

T-SQLで変数を代入するときのSETステートメントとSELECTステートメントの違いは何ですか?

261
juur

引用符 は、 この記事の要約

  1. SETは変数割り当てのANSI標準ですが、SELECTはそうではありません。
  2. SETは一度に1つの変数しか代入できませんが、SELECTは一度に複数の代入を行うことができます。
  3. クエリから代入する場合、SETはスカラ値しか代入できません。クエリが複数の値/行を返す場合、SETはエラーを発生させます。 SELECTは変数の1つを変数に代入し、複数の値が返されたという事実を隠します(したがって、他の場所で問題が発生した理由を知ることはおそらくないでしょう。
  4. 値が返されない場合にクエリから代入すると、SETはNULLを代入します。SELECTは代入をまったく行いません(したがって、変数は以前の値から変更されません)。
  5. 速度の違いに関しては - SETとSELECTの間に直接の違いはありません。ただし、一度に複数の代入を行うSELECTの機能は、SETよりもわずかに速度が向上します。
386
OMG Ponies

私はSETがANSI規格であるのに対し、SELECTは規格ではないと思います。以下の例では、値が見つからない場合のSETSELECTの動作の違いにも注意してください。

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

クエリを書くとき、この違いを覚えておくべきです:

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

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

エラーが発生します。

副照会が複数の値を戻しました。副照会が=、!=、<、<=、>、> =の後に続く場合、または副照会が式として使用される場合、これは許可されません。

なぜあなたは些細な "結果の最後の項目"を@employeeIdに割り当てたいのですか? selectを使用すると、エラーが発生することはなく、デバッグに数分、数時間かかります。

おそらく、あなたは単一のIdを探していて、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:変数に複数の値を代入したい場合。変数は、テーブル、一時テーブル、テーブル変数などです。
2
CodingYoshi