งบส่วนบุคคล - DML, DDL, ฯลฯ - เป็นการทำธุรกรรมในตัวเอง ใช่หลังจากการวนซ้ำแต่ละครั้ง (ในทางเทคนิค: หลังจากแต่ละคำสั่ง) สิ่งที่UPDATE
คำสั่งนั้นเปลี่ยนจะถูกกำหนดโดยอัตโนมัติ
แน่นอนว่ามีข้อยกเว้นเสมอใช่ไหม เป็นไปได้ที่จะเปิดใช้งานธุรกรรมโดยนัยผ่านSET IMPLICIT_TRANSACTIONSซึ่งในกรณีนี้UPDATE
คำสั่งแรกจะเริ่มทำธุรกรรมที่คุณจะต้องทำCOMMIT
หรือROLLBACK
สิ้นสุด นี่คือการตั้งค่าระดับเซสชันที่ปิดโดยปริยายในกรณีส่วนใหญ่
เราจำเป็นต้องเพิ่มคำสั่ง BEGIN TRANSACTION / END TRANSACTION ที่ชัดเจนเพื่อให้เราสามารถยกเลิกได้ตลอดเวลาหรือไม่
No. และในความเป็นจริงที่ระบุว่าคุณต้องการที่จะสามารถที่จะหยุดกระบวนการและเริ่มต้นใหม่ได้เพิ่มการทำธุรกรรมอย่างชัดเจน (หรือช่วยให้การทำธุรกรรมโดยปริยาย) COMMIT
จะเป็นความคิดที่ดีตั้งแต่การหยุดกระบวนการอาจจับมันก่อนที่จะทำ ในกรณีนี้คุณจะต้องออกด้วยตนเองCOMMIT
(ถ้าคุณอยู่ใน SSMS) หรือถ้าคุณกำลังเรียกใช้งานนี้จากงานตัวแทนของ SQL คุณจะไม่มีโอกาสนั้นและอาจจบด้วยธุรกรรมที่ไม่ได้ใช้งาน
นอกจากนี้คุณอาจต้องการตั้งค่า@CHUNK_SIZE
ให้มีจำนวนน้อยลง การเพิ่มการล็อคโดยทั่วไปเกิดขึ้นที่การล็อค 5,000 ครั้งที่วัตถุหนึ่ง ขึ้นอยู่กับขนาดของแถวและหากกำลังทำการล็อกแถวกับการล็อกหน้าคุณอาจกำลังเกินขีด จำกัด นั้น หากขนาดของแถวนั้นมีขนาดเพียง 1 หรือ 2 แถวพอดีกับแต่ละหน้าคุณอาจกดปุ่มนี้แม้ว่าจะทำการล็อกหน้าก็ตาม
หากตารางถูกแบ่งพาร์ติชันแล้วคุณจะมีตัวเลือกในการตั้งค่าLOCK_ESCALATION
ตัวเลือก (นำมาใช้ใน SQL Server 2008) สำหรับตารางAUTO
ที่จะล็อคพาร์ติชันเท่านั้นและไม่ใช่ทั้งตารางเมื่อเลื่อนระดับ หรือสำหรับตารางใด ๆ คุณสามารถตั้งค่าตัวเลือกเดียวกันDISABLE
นั้นได้แม้ว่าคุณจะต้องระวังให้มาก ดูการเปลี่ยนแปลงตารางสำหรับรายละเอียด
นี่คือเอกสารบางส่วนที่พูดถึงการเลื่อนระดับการล็อคและเกณฑ์: การเลื่อนการล็อค (ซึ่งมีการระบุว่าใช้กับ "SQL Server 2008 R2 และรุ่นที่สูงกว่า") และนี่คือบล็อกโพสต์ที่เกี่ยวข้องกับการตรวจสอบและแก้ไขการเพิ่มล็อค: ล็อคใน Microsoft SQL Server (ตอนที่ 12 - ล็อก)
ไม่เกี่ยวข้องกับคำถามที่แน่นอน แต่เกี่ยวข้องกับคำถามในคำถามมีการปรับปรุงเล็กน้อยที่สามารถทำได้ที่นี่ (หรืออย่างน้อยก็ดูเหมือนว่าวิธีการเพียงแค่มองจาก)
สำหรับลูปของคุณการทำWHILE (@@ROWCOUNT = @CHUNK_SIZE)
ดีกว่าเล็กน้อยเนื่องจากถ้าจำนวนแถวที่อัพเดตในการทำซ้ำครั้งล่าสุดน้อยกว่าจำนวนที่ร้องขอเป็น UPDATE ดังนั้นจึงไม่มีงานเหลือให้ทำ
ถ้าdeleted
ข้อมูลเป็นBIT
ประเภทข้อมูลแล้วไม่ว่าค่าที่กำหนดโดยไม่ว่าจะเป็นหรือไม่deletedDate
เป็น2000-01-01
? ทำไมคุณถึงต้องการทั้งสองอย่าง?
หากทั้งสองฟิลด์เป็นฟิลด์ใหม่และคุณเพิ่มเข้าไปเนื่องจากNULL
อาจเป็นการดำเนินการออนไลน์ / ไม่มีการบล็อกและตอนนี้ต้องการอัปเดตเป็นค่า "เริ่มต้น" แล้วนั่นไม่จำเป็น เริ่มต้นใน SQL Server 2012 (Enterprise Edition เท่านั้น) การเพิ่มNOT NULL
คอลัมน์ที่มีข้อ จำกัด DEFAULT เป็นการดำเนินการที่ไม่บล็อกตราบใดที่ค่า DEFAULT เป็นค่าคงที่ ดังนั้นหากคุณยังไม่ได้ใช้ฟิลด์เพียงแค่วางและเพิ่มอีกครั้งNOT NULL
และด้วยข้อ จำกัด เริ่มต้น
หากไม่มีกระบวนการอื่นกำลังอัปเดตฟิลด์เหล่านี้ในขณะที่คุณทำการอัปเดตนี้จะเร็วขึ้นหากคุณจัดคิวระเบียนที่คุณต้องการอัปเดตแล้วเพียงแค่ปิดคิวนั้น มีการเข้าชมประสิทธิภาพในวิธีการปัจจุบันตามที่คุณต้องสอบถามตารางในแต่ละครั้งเพื่อให้ได้ชุดที่ต้องเปลี่ยน แต่คุณสามารถทำสิ่งต่อไปนี้ซึ่งจะสแกนตารางเพียงครั้งเดียวบนสองฟิลด์จากนั้นออกคำสั่ง UPDATE ที่ตรงเป้าหมายมากเท่านั้น นอกจากนี้ยังไม่มีบทลงโทษจากการหยุดกระบวนการในเวลาใดก็ได้และเริ่มต้นในภายหลังเนื่องจากประชากรเริ่มต้นของคิวจะเพียงแค่ค้นหาระเบียนที่เหลือเพื่ออัปเดต
- สร้างตารางชั่วคราว (#FullSet) ที่เพิ่งมีฟิลด์คีย์จากดัชนีคลัสเตอร์ในนั้น
- สร้างตารางชั่วคราวที่สอง (#CurrentSet) ของโครงสร้างเดียวกัน
แทรกลงใน #FullSet ผ่าน SELECT TOP(n) KeyField1, KeyField2 FROM [huge-table] where deleted is null or deletedDate is null;
ที่TOP(n)
อยู่ในนั้นเนื่องจากขนาดของตาราง ด้วย 100 ล้านแถวในตารางคุณไม่จำเป็นต้องเติมตารางคิวด้วยชุดคีย์ทั้งหมดนั้นโดยเฉพาะอย่างยิ่งถ้าคุณวางแผนที่จะหยุดกระบวนการทุก ๆ ครั้งและเริ่มต้นใหม่ในภายหลัง ดังนั้นอาจตั้งค่าn
เป็น 1 ล้านและปล่อยให้สิ่งนั้นผ่านไปสู่ความสำเร็จ คุณสามารถกำหนดเวลานี้ได้ในงานตัวแทน SQL ที่รันชุด 1 ล้าน (หรืออาจน้อยกว่า) จากนั้นรอเวลาถัดไปที่กำหนดเวลาเพื่อรับอีกครั้ง จากนั้นคุณสามารถกำหนดเวลาให้ทำงานทุก ๆ 20 นาทีดังนั้นจะมีห้องหายใจที่บังคับใช้ระหว่างชุดของn
แต่มันจะยังคงเสร็จสิ้นกระบวนการทั้งหมดแบบอัตโนมัติ จากนั้นให้ลบงานออกเมื่อไม่มีอะไรให้ทำ :-)
- ในลูปทำ:
- เติมชุดปัจจุบันผ่านสิ่งที่ชอบ
DELETE TOP (4995) FROM #FullSet OUTPUT Deleted.KeyField INTO #CurrentSet (KeyField);
IF (@@ROWCOUNT = 0) BREAK;
- ทำการอัปเดตโดยใช้บางสิ่งเช่น:
UPDATE ht SET ht.deleted = 0, ht.deletedDate='2000-01-01' FROM [huge-table] ht INNER JOIN #CurrentSet cs ON cs.KeyField = ht.KeyField;
- ล้างชุดปัจจุบัน:
TRUNCATE TABLE #CurrentSet;
- ในบางกรณีจะช่วยเพิ่มดัชนีตัวกรองเพื่อช่วยให้
SELECT
ฟีดนั้นลงใน#FullSet
ตารางอุณหภูมิ ต่อไปนี้เป็นข้อควรพิจารณาบางประการเกี่ยวกับการเพิ่มดัชนีดังกล่าว:
- เงื่อนไข WHERE ควรตรงกับเงื่อนไข WHERE ของข้อความค้นหาของคุณ
WHERE deleted is null or deletedDate is null
- ที่จุดเริ่มต้นของกระบวนการแถวส่วนใหญ่จะตรงกับเงื่อนไข WHERE ของคุณดังนั้นดัชนีไม่เป็นประโยชน์ คุณอาจต้องการรอจนกว่าจะมีเครื่องหมายประมาณ 50% ก่อนที่จะเพิ่มสิ่งนี้ แน่นอนว่าจะช่วยได้มากแค่ไหนและเมื่อใดควรเพิ่มดัชนีให้แตกต่างกันไปเนื่องจากปัจจัยหลายประการดังนั้นจึงเป็นการลองผิดลองถูก
- คุณอาจต้องอัปเดตสถานะด้วยตนเองและ / หรือสร้างดัชนีใหม่ด้วยตนเองเพื่อให้ทันสมัยเนื่องจากข้อมูลฐานมีการเปลี่ยนแปลงค่อนข้างบ่อย
- โปรดจำไว้ว่าดัชนีในขณะที่ช่วยเหลือ
SELECT
นั้นจะทำให้เกิดความเสียหายUPDATE
เนื่องจากเป็นวัตถุอื่นที่ต้องได้รับการอัปเดตในระหว่างการดำเนินการดังนั้นจึงมี I / O มากขึ้น สิ่งนี้เล่นได้ทั้งสองอย่างโดยใช้ดัชนีตัวกรอง (ซึ่งย่อขนาดเมื่อคุณอัปเดตแถวเนื่องจากแถวที่ตรงกับตัวกรองน้อยลง) และรอสักครู่เพื่อเพิ่มดัชนี (ถ้ามันจะไม่เป็นประโยชน์อย่างยิ่งในการเริ่มต้น I / O เพิ่มเติม)
อัปเดต:โปรดดูคำตอบของคำถามที่เกี่ยวข้องกับคำถามนี้สำหรับการนำไปใช้อย่างสมบูรณ์ของสิ่งที่แนะนำข้างต้นรวมถึงกลไกในการติดตามสถานะและยกเลิกอย่างหมดจด: เซิร์ฟเวอร์ sql: อัปเดตฟิลด์บนโต๊ะขนาดใหญ่ในกลุ่มเล็ก ๆ : วิธีการ ความคืบหน้า / สถานะ?