SET และ SELECT เมื่อกำหนดตัวแปร?


คำตอบ:


412

อ้างอิงซึ่งสรุปจากบทความนี้ :

  1. SET เป็นมาตรฐาน ANSI สำหรับการกำหนดตัวแปร SELECT ไม่ได้
  2. ตลาดหลักทรัพย์สามารถกำหนดตัวแปรได้ครั้งละหนึ่งตัวเท่านั้น SELECT สามารถทำการมอบหมายได้หลายรายการพร้อมกัน
  3. หากกำหนดจากแบบสอบถามตลาดหลักทรัพย์สามารถกำหนดค่าสเกลาร์ได้เท่านั้น หากแบบสอบถามส่งคืนค่า / หลายแถว SET จะเพิ่มข้อผิดพลาด SELECT จะกำหนดหนึ่งในค่าให้กับตัวแปรและซ่อนความจริงที่ว่ามีการส่งคืนค่าหลายค่า (ดังนั้นคุณอาจไม่เคยรู้เลยว่าทำไมบางอย่างผิดปกติที่อื่น - สนุกกับการแก้ปัญหาที่เกิดขึ้น)
  4. เมื่อกำหนดจากแบบสอบถามถ้าไม่มีค่าส่งคืน SET จะกำหนดค่า NULL โดยที่ SELECT จะไม่ทำการมอบหมายเลย (ดังนั้นตัวแปรจะไม่ถูกเปลี่ยนจากค่าก่อนหน้า)
  5. เท่าที่ความแตกต่างของความเร็ว - ไม่มีความแตกต่างโดยตรงระหว่าง SET และ SELECT อย่างไรก็ตามความสามารถของ SELECT ในการทำการมอบหมายหลายครั้งในหนึ่งช็อตทำให้ได้เปรียบในเรื่องความเร็วเหนือ SET เล็กน้อย

3
ฉันไม่ได้ลงคะแนน แต่สิ่งต่อไปนี้ไม่ถูกต้อง: "เท่าที่ความแตกต่างของความเร็ว - ไม่มีความแตกต่างโดยตรงระหว่าง SET และ SELECT" หากคุณกำหนดค่าหลายค่าในหนึ่งเดียวนั่นอาจเร็วกว่านั้นผ่านชุดค่า maultiple Google up "การกำหนดตัวแปรหลายตัวด้วยหนึ่ง SELECT ทำงานได้เร็วขึ้น"
AK

14
@AlexKuznetsov: ประโยคหลังจากนั้นพูดอย่างนั้น
OMG Ponies

3
@OMG Ponies: เร็วกว่าหรือมากกว่า 10 เท่าดังนั้นฉันไม่แน่ใจว่ามันเป็น "ความได้เปรียบความเร็วเล็กน้อย"
AK

2
โดยเฉพาะอย่างยิ่งเมื่อใช้ในขณะที่ลูปฉันได้เห็นประสิทธิภาพการทำงานที่เพิ่มขึ้นอย่างมากโดยการตั้งค่า / เริ่มต้นตัวแปรทั้งหมดของฉันใหม่โดยใช้ one-Select กับชุดจำนวนมาก ฉันยังสามารถรวม Variable-Logic ของฉันใน Select to all ที่รันพร้อมกันได้ด้วยเช่น: SELECT @Int = @Int + 1, @Int = @Int + 1ถ้า@Intเริ่มจาก 0 มันจะจบลงที่ 2 ซึ่งจะมีประโยชน์มากเมื่อทำการจัดการสตริงต่อเนื่องกัน
MikeTeeVee

การอภิปรายที่น่าสนใจเกี่ยวกับความแตกต่างด้านประสิทธิภาพ หากการตั้งค่าหลายค่าผ่านการเลือกเร็วขึ้น (เป็นรหัสและดำเนินการ) วิธีหนึ่งที่ไม่ปลอดภัยเพื่อหลีกเลี่ยงจุดที่ 4 (ค่าตัวแปรของคุณล้มเหลวในการเปลี่ยนแปลงหากเคียวรีคืนค่า null) คือการตั้งค่าตัวแปรของคุณเป็นค่าว่างก่อนการเลือก เมื่อคุณคำนึงถึงสิ่งนั้นทั้งสองจะเปรียบเทียบประสิทธิภาพอย่างไร (หมายเหตุด้านข้าง: ฉันไม่เข้าใจเหตุผลที่เลือกไม่ตั้งค่าตัวแปรของคุณเป็นโมฆะในกรณีที่แบบสอบถามส่งคืน null เมื่อใดที่คุณต้องการหรือไม่)
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 การรันครั้งเดียวจะดีกว่าเพื่อให้เข้าใจตรวจสอบเล่นจดจำว่าต้องอ่าน แต่คำตอบอื่น ๆ เป็นเพียงข้อความ
Gennady Vanin ГеннадийВанин

4
หากคุณใช้งานจริงselect @var = (select name from master.sys.tables where name = 'qwerty')คุณจะได้รับ @var เป็นโมฆะ ตัวอย่างที่คุณให้ไม่ใช่แบบสอบถามเดียวกัน
Zack

4
คุณมี(select name from master.sys.tables where name = 'qwerty')หนึ่งสำหรับและname from master.sys.tables where name = 'qwerty'อื่น ๆ ... คุณไม่เห็นว่า?
Zack

4
@ Zack: แต่ละรายการเป็นไวยากรณ์ที่ถูกต้องสำหรับสิ่งที่ฉันพยายามสาธิต; ความแตกต่างระหว่างการใช้ SET กับ SELECT เพื่อกำหนดค่าให้กับตัวแปรเมื่อเคียวรีที่อยู่ภายใต้ไม่มีผลลัพธ์
Joe Stefanelli

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
Zack

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คุณจะไม่ได้รับข้อผิดพลาดใด ๆ และคุณจะใช้เวลาหลายนาที, การแก้จุดบกพร่องชั่วโมง

บางทีคุณกำลังมองหา 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: เมื่อคุณต้องการกำหนดค่าหลายค่าให้กับตัวแปร ตัวแปรอาจเป็นตาราง, ตารางชั่วคราวหรือตัวแปรตารางเป็นต้น
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.