หากฉันมีคำสั่ง UPDATE ที่ไม่เปลี่ยนแปลงข้อมูลใด ๆ (เนื่องจากข้อมูลอยู่ในสถานะที่อัพเดตแล้ว) จะมีประโยชน์ด้านประสิทธิภาพใด ๆ ในการวางเช็คในส่วนคำสั่งเพื่อป้องกันการอัพเดทหรือไม่?
อาจเป็นเพราะมีความแตกต่างของประสิทธิภาพเล็กน้อยเนื่องจากUPDATE 1 :
- ไม่ได้อัปเดตแถวใด ๆ (ดังนั้นจึงไม่มีอะไรให้เขียนลงดิสก์ไม่แม้แต่บันทึกกิจกรรมน้อยที่สุด) และ
- ทำการล็อกที่ จำกัด น้อยกว่าสิ่งที่จำเป็นสำหรับการทำการปรับปรุงจริง (ดีกว่าสำหรับการทำงานพร้อมกัน) ( โปรดดูส่วนการปรับปรุงไปยังจุดสิ้นสุด )
อย่างไรก็ตามคุณจำเป็นต้องวัดความแตกต่างของจำนวนเท่าใดในระบบของคุณด้วยสคีมาของคุณและข้อมูลและโหลดระบบ มีปัจจัยหลายอย่างที่ส่งผลกระทบต่อ UPDATE ที่ไม่ได้อัปเดตที่มีผลกระทบ:
- จำนวนของการช่วงชิงบนโต๊ะที่กำลังอัพเดต
- จำนวนแถวที่มีการปรับปรุง
- หากมีทริกเกอร์ UPDATE ในตารางที่กำลังอัปเดต (ตามที่ระบุไว้โดยทำเครื่องหมายในความคิดเห็นในคำถาม) หากคุณดำเนิน
UPDATE TableName SET Field1 = Field1
การ Update Trigger จะเริ่มทำงานและระบุว่ามีการอัปเดตฟิลด์ (หากคุณตรวจสอบโดยใช้ฟังก์ชันUPDATE ()หรือCOLUMNS_UPDATED ) และฟิลด์ในทั้งสองINSERTED
และDELETED
ตารางเป็นค่าเดียวกัน
นอกจากนี้ส่วนสรุปต่อไปนี้พบได้ในบทความของพอลไวท์เรื่องThe Impact of Non-Updating Updates (ตามที่ระบุไว้โดย @spaghettidba ในความคิดเห็นเกี่ยวกับคำตอบของเขา):
SQL Server มีจำนวนของการปรับให้เหมาะสมเพื่อหลีกเลี่ยงการบันทึกที่ไม่จำเป็นหรือการล้างหน้าเมื่อประมวลผลการดำเนินการ UPDATE ที่จะไม่ส่งผลให้เกิดการเปลี่ยนแปลงใด ๆ กับฐานข้อมูลถาวร
- โดยทั่วไปการไม่อัปเดตการอัปเดตไปยังตารางคลัสเตอร์จะหลีกเลี่ยงการบันทึกเพิ่มเติมและการล้างหน้ายกเว้นคอลัมน์ที่มีรูปแบบ (ส่วนหนึ่ง) คีย์คลัสเตอร์จะได้รับผลกระทบจากการดำเนินการอัปเดต
- หากส่วนใด ๆ ของคีย์คลัสเตอร์นั้น 'อัพเดท' เป็นค่าเดียวกันการดำเนินการจะถูกบันทึกราวกับว่าข้อมูลมีการเปลี่ยนแปลงและหน้าเว็บที่ได้รับผลกระทบจะถูกทำเครื่องหมายว่าสกปรกในพูลบัฟเฟอร์ นี่คือผลลัพธ์ของการแปลง UPDATE เป็นการดำเนินการลบแล้วแทรก
- ตารางฮีปจะทำงานเหมือนกับตารางที่ทำคลัสเตอร์ยกเว้นจะไม่มีคีย์คลัสเตอร์เพื่อทำให้การบันทึกเพิ่มเติมหรือการล้างหน้า กรณีนี้ยังคงอยู่แม้ว่าจะมีคีย์หลักที่ไม่ได้ทำคลัสเตอร์อยู่บนฮีป การไม่อัปเดตการอัปเดตไปที่ฮีปจึงมักหลีกเลี่ยงการบันทึกและการล้างข้อมูลเพิ่มเติม (แต่ดูด้านล่าง)
- ทั้งฮีปและตารางคลัสเตอร์จะได้รับการบันทึกและฟลัชพิเศษสำหรับแถวใด ๆ ที่คอลัมน์ LOB ที่มีข้อมูลมากกว่า 8000 ไบต์ได้รับการอัปเดตเป็นค่าเดียวกันโดยใช้ไวยากรณ์ใด ๆ นอกเหนือจาก 'SET column_name = column_name'
- เพียงแค่เปิดใช้งานระดับการแยกการกำหนดเวอร์ชันของแถวทั้งสองบนฐานข้อมูลจะทำให้เกิดการบันทึกและล้างข้อมูลเพิ่มเติมเสมอ สิ่งนี้เกิดขึ้นโดยไม่คำนึงถึงระดับการแยกที่มีผลสำหรับธุรกรรมการอัพเดท
โปรดทราบ (โดยเฉพาะอย่างยิ่งถ้าคุณไม่ติดตามลิงค์เพื่อดูบทความเต็มของ Paul) รายการสองรายการต่อไปนี้:
การอัปเดตที่ไม่อัปเดตยังคงมีกิจกรรมการบันทึกบางรายการซึ่งแสดงว่าธุรกรรมกำลังเริ่มต้นและสิ้นสุด เป็นเพียงการที่ไม่มีการดัดแปลงข้อมูลใด ๆ เกิดขึ้น (ซึ่งยังคงเป็นการประหยัดที่ดี)
ตามที่ระบุไว้ข้างต้นคุณต้องทดสอบระบบของคุณ ใช้แบบสอบถามการวิจัยเดียวกับที่ Paul ใช้และดูว่าคุณได้ผลลัพธ์เดียวกันหรือไม่ ฉันเห็นผลลัพธ์ที่แตกต่างกันเล็กน้อยในระบบของฉันมากกว่าที่แสดงในบทความ ยังไม่มีหน้าที่เขียนสกปรก แต่มีกิจกรรมการบันทึกเพิ่มเติมเล็กน้อย
... ฉันต้องการจำนวนแถวเพื่อรวมแถวที่ไม่เปลี่ยนแปลงดังนั้นฉันจึงรู้ว่าจะแทรกถ้าไม่มี ID อยู่หรือไม่ ... เป็นไปได้ไหมที่ฉันจะนับแถวที่ฉันต้องการอย่างใด
หากคุณจัดการกับแถวเดียวคุณสามารถทำสิ่งต่อไปนี้:
UPDATE MyTable
SET Value = 2
WHERE ID = 2
AND Value <> 2;
IF (@@ROWCOUNT = 0)
BEGIN
IF (NOT EXISTS(
SELECT *
FROM MyTable
WHERE ID = 2 -- or Value = 2 depending on the scenario
)
)
BEGIN
INSERT INTO MyTable (ID, Value) -- or leave out ID if it is an IDENTITY
VALUES (2, 2);
END;
END;
สำหรับหลายแถวคุณสามารถรับข้อมูลที่จำเป็นในการตัดสินใจโดยใช้OUTPUT
ข้อ คุณสามารถ จำกัด รายการให้แคบลงเพื่อค้นหาความแตกต่างระหว่างการไม่อัปเดตแถวที่ไม่มีอยู่ตรงข้ามกับการไม่อัปเดตแถวที่มีอยู่ แต่ไม่ต้องการการอัปเดต
ฉันแสดงการใช้งานพื้นฐานในคำตอบต่อไปนี้:
วิธีหลีกเลี่ยงการใช้แบบสอบถามแบบผสานเมื่อทำซ้ำหลายข้อมูลโดยใช้พารามิเตอร์ xml
วิธีที่แสดงในคำตอบนั้นไม่ได้กรองแถวที่มีอยู่ แต่ไม่จำเป็นต้องได้รับการปรับปรุง สามารถเพิ่มส่วนดังกล่าวได้ แต่ก่อนอื่นคุณต้องแสดงให้เห็นอย่างชัดเจนว่าคุณได้รับชุดข้อมูลของคุณที่คุณรวมเข้าไว้ที่MyTable
ใด พวกเขามาจากตารางชั่วคราวหรือไม่? พารามิเตอร์ที่มีค่าเป็นตาราง (TVP) หรือไม่
อัปเดต 1:
ในที่สุดฉันก็สามารถทำการทดสอบได้และนี่คือสิ่งที่ฉันพบเกี่ยวกับบันทึกธุรกรรมและการล็อก ก่อนคีมาสำหรับตาราง:
CREATE TABLE [dbo].[Test]
(
[ID] [int] NOT NULL CONSTRAINT [PK_Test] PRIMARY KEY CLUSTERED,
[StringField] [varchar](500) NULL
);
ถัดไปการทดสอบจะอัปเดตฟิลด์เป็นค่าที่มีอยู่แล้ว:
UPDATE rt
SET rt.StringField = '04CF508B-B78E-4264-B9EE-E87DC4AD237A'
FROM dbo.Test rt
WHERE rt.ID = 4082117
ผล:
-- Transaction Log (2 entries):
Operation
----------------------------
LOP_BEGIN_XACT
LOP_COMMIT_XACT
-- SQL Profiler (3 Lock:Acquired events):
Mode Type
--------------------------------------
8 - IX 5 - OBJECT
8 - IX 6 - PAGE
5 - X 7 - KEY
ในที่สุดการทดสอบที่กรองการอัพเดตเนื่องจากค่าไม่เปลี่ยนแปลง:
UPDATE rt
SET rt.StringField = '04CF508B-B78E-4264-B9EE-E87DC4AD237A'
FROM dbo.Test rt
WHERE rt.ID = 4082117
AND rt.StringField <> '04CF508B-B78E-4264-B9EE-E87DC4AD237A';
ผล:
-- Transaction Log (0 entries):
Operation
----------------------------
-- SQL Profiler (3 Lock:Acquired events):
Mode Type
--------------------------------------
8 - IX 5 - OBJECT
7 - IU 6 - PAGE
4 - U 7 - KEY
อย่างที่คุณเห็นไม่มีสิ่งใดถูกเขียนลงในบันทึกการทำธุรกรรมเมื่อกรองแถวเมื่อเทียบกับสองรายการที่ทำเครื่องหมายจุดเริ่มต้นและจุดสิ้นสุดของธุรกรรม และในขณะที่มันเป็นความจริงที่ทั้งสองรายการที่เกือบจะไม่มีอะไรพวกเขายังคงเป็นบางสิ่งบางอย่าง
นอกจากนี้การล็อกทรัพยากร PAGE และ KEY ก็มีข้อ จำกัด น้อยลงเมื่อกรองแถวที่ไม่ได้เปลี่ยนแปลง หากไม่มีกระบวนการอื่นใดที่โต้ตอบกับตารางนี้แสดงว่ามันอาจจะไม่ใช่ประเด็น (แต่มีความเป็นไปได้จริงหรือ โปรดทราบว่าการทดสอบที่แสดงในบล็อกที่เชื่อมโยงใด ๆ (และแม้แต่การทดสอบของฉัน) ถือว่าโดยนัยว่าไม่มีการโต้แย้งบนโต๊ะเนื่องจากไม่เคยเป็นส่วนหนึ่งของการทดสอบ การบอกว่าการอัพเดทที่ไม่ได้อัพเดตนั้นมีน้ำหนักเบาจนไม่ต้องจ่ายเงินในการทำการกรองจะต้องใช้เกลือเม็ดหนึ่งเนื่องจากการทดสอบเสร็จแล้วมากหรือน้อยในสุญญากาศ แต่ในการผลิตตารางนี้ส่วนใหญ่จะไม่โดดเดี่ยว แน่นอนว่าอาจเป็นได้ว่าการบันทึกเล็กน้อยและการล็อคที่ จำกัด มากขึ้นไม่ได้แปลว่ามีประสิทธิภาพน้อยลง ดังนั้นแหล่งข้อมูลที่น่าเชื่อถือที่สุดที่จะตอบคำถามนี้? เซิร์ฟเวอร์ SQL โดยเฉพาะ:เซิร์ฟเวอร์ SQL ของคุณ มันจะแสดงวิธีที่ดีกว่าสำหรับระบบของคุณ :-)
อัปเดต 2:
หากการดำเนินการที่ค่าใหม่เหมือนกันกับค่าปัจจุบัน (เช่นไม่มีการอัพเดต) ให้ระบุจำนวนการดำเนินการที่ค่าใหม่นั้นแตกต่างกันและจำเป็นต้องมีการอัปเดตดังนั้นรูปแบบต่อไปนี้อาจพิสูจน์ได้ว่าดียิ่งขึ้นโดยเฉพาะอย่างยิ่ง มีข้อโต้แย้งมากมายบนโต๊ะ แนวคิดก็คือทำสิ่งที่ง่ายSELECT
ก่อนเพื่อให้ได้ค่าปัจจุบัน INSERT
ถ้าคุณไม่ได้รับค่าแล้วคุณมีคำตอบของคุณเกี่ยวกับ หากคุณมีค่าที่คุณสามารถทำได้ง่ายIF
และออกUPDATE
เท่านั้นถ้ามันเป็นสิ่งจำเป็น
DECLARE @CurrentValue VARCHAR(500) = NULL,
@NewValue VARCHAR(500) = '04CF508B-B78E-4264-B9EE-E87DC4AD237A',
@ID INT = 4082117;
SELECT @CurrentValue = rt.StringField
FROM dbo.Test rt
WHERE rt.ID = @ID;
IF (@CurrentValue IS NULL) -- if NULL is valid, use @@ROWCOUNT = 0
BEGIN
-- row does not exist
INSERT INTO dbo.Test (ID, StringField)
VALUES (@ID, @NewValue);
END;
ELSE
BEGIN
-- row exists, so check value to see if it is different
IF (@CurrentValue <> @NewValue)
BEGIN
-- value is different, so do the update
UPDATE rt
SET rt.StringField = @NewValue
FROM dbo.Test rt
WHERE rt.ID = @ID;
END;
END;
ผล:
-- Transaction Log (0 entries):
Operation
----------------------------
-- SQL Profiler (2 Lock:Acquired events):
Mode Type
--------------------------------------
6 - IS 5 - OBJECT
6 - IS 6 - PAGE
ดังนั้นจึงมีเพียง 2 ล็อคที่ได้รับแทนที่จะเป็น 3 และล็อคทั้งสองนี้เป็น Intent Shared ไม่ใช่ Intent eXclusive หรือ Intent Update ( ความเข้ากันได้ของล็อค ) โปรดทราบว่าการล็อกแต่ละครั้งที่ได้รับจะได้รับการปล่อยตัวด้วยการล็อกแต่ละครั้งเป็นการดำเนินการ 2 ครั้งดังนั้นวิธีการใหม่นี้จึงเป็นการรวม 4 การดำเนินการแทนที่จะเป็นการดำเนินการ 6 ครั้งในวิธีการที่เสนอไว้ครั้งแรก เมื่อพิจารณาการดำเนินการนี้จะทำงานหนึ่งครั้งทุกๆ 15 มิลลิวินาที (ประมาณตามที่ระบุโดย OP) นั่นคือประมาณ 66 ครั้งต่อวินาที ดังนั้นข้อเสนอดั้งเดิมจึงมีจำนวนการดำเนินการล็อค / ปลดล็อก 396 ต่อวินาทีในขณะที่วิธีการใหม่นี้มีจำนวนการดำเนินการล็อค / ปลดล็อกเพียง 264 ต่อวินาทีของการล็อคน้ำหนักที่เบากว่า นี่ไม่ใช่การรับประกันประสิทธิภาพที่ยอดเยี่ยม แต่ก็คุ้มค่ากับการทดสอบ :-)