ย้ายแถวจากตารางหนึ่งไปอีกตารางหนึ่ง


9

ฉันกำลังย้ายระเบียนจากฐานข้อมูลหนึ่งไปยังอีกฐานข้อมูลซึ่งเป็นส่วนหนึ่งของกระบวนการเก็บถาวร ฉันต้องการคัดลอกแถวไปยังตารางปลายทางแล้วลบแถวเดียวกันออกจากตารางต้นฉบับ

คำถามของฉันคือวิธีที่มีประสิทธิภาพที่สุดในการตรวจสอบว่าการแทรกครั้งแรกสำเร็จหรือไม่ก่อนที่จะลบแถว

ความคิดของฉันคือสิ่งนี้ แต่ฉันรู้สึกว่ามีวิธีที่ดีกว่า:

@num_records=select count(ID) from Source_Table where (criteria for eligible rows)

insert * into Destination_Table where (criteria for eligible rows)

if ((select count(ID) from Destination_Table where (criteria) )=@numrecords)

delete * from Source_Table where (criteria)

จะดีกว่า / เป็นไปได้ที่จะรวมเข้ากับฟังก์ชั่น RAISERROR หรือไม่? ขอบคุณ!

คำตอบ:


13

ฉันจะแนะนำไวยากรณ์TRY / CATCHพร้อมกับการทำธุรกรรมที่ชัดเจน ข้อสันนิษฐานของฉันสำหรับการแก้ปัญหานี้คือเหตุผลที่ทำให้การแทรกล้มเหลวเป็นข้อผิดพลาด SQL ที่ตรวจสอบได้ (เช่นการละเมิดคีย์ข้อผิดพลาดประเภทข้อมูลไม่ตรงกัน / ข้อผิดพลาดในการแปลง ฯลฯ ) โครงสร้างจะมีลักษณะเช่นนี้:

BEGIN TRAN

BEGIN TRY
  INSERT INTO foo(col_a,col_b,col_c,recdate)
  SELECT col_a,col_b,col_c,recdate
  FROM bar
  WHERE recdate BETWEEN @startdate AND @enddate

  DELETE FROM bar
  WHERE recdate BETWEEN @startdate AND @enddate

  COMMIT TRAN
END TRY
BEGIN CATCH
  ROLLBACK TRAN
END CATCH

วิธีการทำงานของโครงสร้างนี้หากเกิดข้อผิดพลาดใน INSERT หรือ DELETE การกระทำทั้งหมดจะถูกย้อนกลับ สิ่งนี้รับประกันได้ว่าการกระทำทั้งหมดจะต้องสำเร็จให้เสร็จสมบูรณ์ หากคุณรู้สึกว่าจำเป็นคุณสามารถรวมเข้ากับTHROWสำหรับ 2012 หรือRAISERRORในปี 2551 และก่อนหน้าเพื่อเพิ่มตรรกะเพิ่มเติมและบังคับให้มีการย้อนกลับหากไม่พบตรรกะดังกล่าว

อีกตัวเลือกหนึ่งคือดูSET XACT_ABORT ONแต่ฉันรู้สึกว่าไวยากรณ์ TRY / CATCH ให้ความละเอียดมากขึ้น


19

ถ้าตารางเก็บถาวรของคุณไม่ได้

  • เปิดใช้งานทริกเกอร์ที่กำหนดไว้
  • เข้าร่วมทั้งสองด้านของข้อ จำกัด KEY ต่างประเทศ
  • มีข้อ จำกัด ในการตรวจสอบหรือกฎที่เปิดใช้งาน

คุณสามารถทำได้ในหนึ่ง statenent

DELETE FROM source_table
OUTPUT deleted.Foo,
       deleted.Bar,
       SYSUTCDATETIME()
INTO archive_table(Foo, Bar, archived)
WHERE  Foo = 1; 

สิ่งนี้จะประสบความสำเร็จหรือล้มเหลวในฐานะหน่วยและยังหลีกเลี่ยงสภาพการแข่งขันที่เป็นไปได้ด้วยการเพิ่มแถวระหว่างINSERTการเก็บถาวรและDELETE(แม้ว่าWHEREประโยคของคุณอาจทำให้สิ่งนี้ไม่น่าเป็นไปได้อย่างยิ่ง)


ไม่มีการข้างต้น ฉันคิดว่าฉันสามารถไปเส้นทางนั้นฉันชอบความเรียบง่ายของโค้ด ฉันไม่ต้องการที่จะสูญเสียบันทึกถ้าแทรกล้มเหลวด้วยเหตุผลใด ๆ (เช่น: ล็อคตารางหมดเวลา ฯลฯ ) ขอบคุณ!
Dina

@Dina - คุณระบุว่าเป็นไปได้ด้วยOUTPUTประโยค? ไม่ใช่เพราะมันเป็นคำสั่งเดียวทั้งหมด นอกจากนี้ยังหลีกเลี่ยงปัญหาการต้องอ่านแถวสองครั้ง (และอาจสูญเสียแถวที่เพิ่มระหว่างการอ่านการแทรกและการอ่านเพื่อการลบ)
Martin Smith

ใช่นั่นคือสิ่งที่ฉันหมายถึง ขอบคุณฉันเห็นจุดของคุณ
Dina

FWIW - วิธีนี้จะทำให้การเติบโตของ logfile ใกล้เคียงกับขนาดของตารางต้นฉบับ ตรวจสอบให้แน่ใจว่าคุณสามารถอยู่กับสิ่งนั้นได้ หากคุณไม่สามารถแบ่งมันออกเป็นแบตช์ด้วย DELETE TOP (N) และห่วงลูปในขณะที่ตรวจสอบตัวแปร @@ rowcount
Wjdavis5

1

วิธีที่ฉันคิดเกี่ยวกับการเก็บถาวร (ซึ่งฉันแน่ใจว่าไม่สมบูรณ์เช่นกัน) คือการเพิ่มคอลัมน์บิตลงในตารางเก็บถาวรใหม่เช่น 'เก็บถาวร' ซึ่งจะมีค่า 1 หลังจากถ่ายโอนบันทึกสำเร็จ และเมื่อคุณถ่ายโอนข้อมูลทั้งหมดคุณสามารถทำการลบขณะที่ค้นหาค่าฟิลด์ 'เก็บถาวร' ที่ '1' เช่น True จากตารางที่เก็บถาวร

และฉันเห็นด้วยกับไมค์ในการใช้ลอง / จับ


1

ลองสิ่งนี้:

INSERT dbo.newtable(
      name,
      department,
      Salary
) SELECT 
            name,
            FirstName,
            Lastname
      FROM    (
           DELETE dbo.oldtable
           OUTPUT
                   DELETED.name,
                   DELETED.department,
                   DELETED.Salary
           WHERE ID  IN ( 1001, 1003, 1005 )
      ) AS RowsToMove;

SELECT * FROM dbo.newtable;
SELECT * FROM dbo.oldtable;
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.