ควรจัดการการลบในฐานข้อมูลอย่างไร


44

ฉันต้องการใช้คุณสมบัติ "ยกเลิกการลบ" ในเว็บแอปพลิเคชันซึ่งผู้ใช้สามารถเปลี่ยนใจและกู้คืนบันทึกที่ถูกลบได้ คิดว่าจะใช้งานอย่างไร ตัวเลือกบางตัวที่ฉันคิดว่าจริง ๆ แล้วเป็นการลบระเบียนที่เป็นปัญหาและจัดเก็บการเปลี่ยนแปลงในตารางตรวจสอบแยกต่างหากหรือไม่ลบระเบียนและใช้คอลัมน์ "ลบ" บูลีนเพื่อทำเครื่องหมายว่าถูกลบ โซลูชันหลังต้องการตรรกะเพิ่มเติมของแอปพลิเคชันเพื่อละเว้นระเบียน "ลบ" ภายใต้สถานการณ์ปกติ แต่จะทำให้ง่ายต่อการใช้การกู้คืนระเบียนในด้านแอปพลิเคชัน


ฉันลืมที่จะพูดถึงว่าในกรณีที่สองระเบียนที่ถูกตั้งค่าสถานะจะต้องถูกลบหรือย้ายหลังจากช่วงเวลาที่ผ่านไปตามสมควร
Abie

คุณใช้ฐานข้อมูลใด
Evan Carroll

ตารางชั่วคราวเป็นทางออกที่ดีที่สุดสำหรับ SQL Server 2016 ขึ้นไป
Sameer

คำตอบ:


37

ใช่ฉันจะเลือกตัวเลือกที่สองแน่นอน แต่ฉันจะเพิ่มเขตข้อมูลวันที่เพิ่มเติม

ดังนั้นคุณเพิ่ม:

delete       boolean
delete_date  timestamp

มันจะช่วยให้คุณมีเวลาสำหรับการยกเลิกการลบ

หากเวลาน้อยกว่าหนึ่งชั่วโมงสามารถยกเลิกการลบได้

ในการลบรายการที่ถูกลบจริงๆเพียงแค่สร้างขั้นตอนการจัดเก็บที่จะทำความสะอาดทุกรายการด้วยการลบการตั้งค่าเป็นจริงและเวลามากกว่าหนึ่งชั่วโมงและทำให้มันเป็นแท็บ cron ที่ทำงานทุก 24 ชั่วโมง

ชั่วโมงเป็นเพียงตัวอย่าง


หรือคุณอาจมีการตั้งค่าสถานะอื่น - cleanedหรือบางอย่าง - ซึ่งระบุว่าข้อมูลที่เกี่ยวข้องกับบันทึกนี้ถูกลบอย่างถูกต้องครบถ้วน บันทึกสามารถยกเลิกการลบได้เว้นแต่cleanedจะเป็นจริงซึ่งในกรณีนี้ไม่สามารถกู้คืนได้
Gaurav

14
นี่เป็นวิธีการทั่วไป ฉันมักจะใช้เขตข้อมูลเดียวdeleted_atถือทั้งความหมายของdeleteบูลีนและdelete_dateเวลา หากdeleted_atมีNULLการจัดการกรณีที่deleteเป็นFALSEและdelete_dateเป็นNULL, deleted_atที่มีการจัดการที่ประทับเวลากรณีที่deleteเป็นTRUEและdelete_dateมีการประทับเวลาช่วยประหยัดเวลาการจัดเก็บข้อมูลและการประยุกต์ใช้ตรรกะ
Julien

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

หมายเหตุ: ลบเป็นคำสงวนใน MySQL
Jason Rikard

โปรดจำไว้ว่าดัชนีที่กรองแล้วในdeletedฟิลด์ของคุณสามารถปรับปรุงประสิทธิภาพได้อย่างมากเมื่อคุณทำการสอบถามแถวที่ไม่มีการลบ
Ross Presser

21

ในการใช้งานของเราที่เราทำไม่ได้จริงๆอะไรลบที่ผู้ใช้ขอล่ะค่ะ (ลูกค้าของเราอยู่ในสภาพแวดล้อมที่มีการควบคุมที่ลบอะไรที่อาจจะนำไปสู่ปัญหาทางกฎหมาย)

เราเก็บเวอร์ชันเก่าไว้ในตารางตรวจสอบแยกต่างหาก (ดังนั้นสำหรับตาราง some_table ซึ่งเป็นตารางที่ชื่อว่า some_table_audit) ซึ่งเหมือนกันนอกเหนือจากการมีตัวระบุเวอร์ชันเพิ่มเติม (การประทับเวลาหากฐานข้อมูลของคุณสนับสนุนค่าเวลาที่ละเอียดมากพอหมายเลขเวอร์ชันเต็ม หรือ UUID ที่เป็น foreign key ไปยังตารางการตรวจสอบทั่วไปและอื่น ๆ ) และอัปเดตตารางการตรวจสอบอัตโนมัติโดยการทริกเกอร์ (ดังนั้นเราไม่จำเป็นต้องสร้างรหัสทั้งหมดที่อัพเดตเรกคอร์ดที่ทราบถึงข้อกำหนดการตรวจสอบ)

ทางนี้:

  • การดำเนินการลบเป็นเพียงการลบแบบง่าย - ไม่จำเป็นต้องเพิ่มรหัสพิเศษใด ๆ ลงในนั้น (แม้ว่าคุณอาจต้องการบันทึกผู้ที่ขอให้ลบแถวใดแม้ว่าพวกเขาจะไม่ถูกลบจริงๆ)
  • ส่วนแทรกและอัพเดตนั้นง่ายเหมือนกัน
  • คุณสามารถใช้การยกเลิกการลบหรือเปลี่ยนกลับโดยเพียงแค่กลับแถว "ปกติ" ไปเป็นเวอร์ชั่นเก่า (ทริกเกอร์การตรวจสอบจะเริ่มต้นอีกครั้งดังนั้นตารางหลักฐานการตรวจสอบจะสะท้อนถึงการเปลี่ยนแปลงนี้ด้วย)
  • คุณสามารถเสนอโอกาสในการตรวจสอบหรือย้อนกลับเป็นเวอร์ชันที่ผ่านมาไม่เพียง แต่ยกเลิกการลบเวอร์ชันล่าสุด
  • คุณไม่ต้องเพิ่ม "ถูกทำเครื่องหมายว่าถูกลบหรือไม่" ตรวจสอบทุกจุดรหัสที่อ้างถึงตารางในคำถามหรือตรรกะ "คัดลอกการตรวจสอบการปรับปรุง" ทุกจุดรหัสที่ลบ / ปรับปรุงแถว (แม้ว่าคุณจะต้องตัดสินใจว่าจะทำอย่างไรกับแถวที่ถูกลบในตารางการตรวจสอบ: เรามี ลบ / ไม่ตั้งค่าสถานะสำหรับแต่ละรุ่นที่นั่นดังนั้นจึงไม่มีหลุมในประวัติหากบันทึกถูกลบและยกเลิกการลบในภายหลัง)
  • การเก็บสำเนาการตรวจสอบในตารางแยกหมายความว่าคุณสามารถแบ่งพาร์ติชันออกเป็นกลุ่มไฟล์ต่าง ๆ ได้อย่างง่ายดาย

หากใช้การประทับเวลาแทน (หรือรวมถึง) หมายเลขเวอร์ชันจำนวนเต็มคุณสามารถใช้ตัวเลือกนี้เพื่อลบสำเนาที่เก่ากว่าหลังจากระยะเวลาที่กำหนดหากจำเป็น แต่พื้นที่ดิสก์นั้นค่อนข้างถูกในทุกวันนี้ดังนั้นหากเราไม่มีเหตุผลที่จะทิ้งข้อมูลเก่า (เช่นกฎการป้องกันข้อมูลที่บอกว่าคุณควรลบข้อมูลลูกค้าหลังจาก X เดือน / ปี) เราจะไม่ทำเช่นนั้น


คำตอบนี้ได้ประมาณสองสามปีและสิ่งสำคัญสองสามอย่างที่อาจส่งผลกระทบต่อการวางแผนประเภทนี้มีการเปลี่ยนแปลงตั้งแต่นั้นมา ฉันจะไม่ลงลึกในรายละเอียดมากนัก แต่เพื่อประโยชน์ของผู้อ่านในวันนี้:

  • SQL Server 2016 เปิดตัว "systemal temporal table" ซึ่งทำงานนี้ให้คุณมากมายและอื่น ๆ อีกมากมายเนื่องจากมีการสร้างน้ำตาล syntactic ที่ดีเพื่อทำให้การสืบค้นและสร้างประวัติศาสตร์ง่ายขึ้นและประสานงานชุดย่อยของการเปลี่ยนแปลงสคีมาระหว่าง ตารางฐานและประวัติ พวกเขาไม่ได้อยู่โดยไม่มีคำเตือน แต่เป็นเครื่องมือที่ทรงพลังสำหรับจุดประสงค์นี้ คุณสมบัติที่คล้ายกันนี้ยังมีอยู่ในระบบฐานข้อมูลอื่น ๆ

  • การเปลี่ยนแปลงกฎหมายคุ้มครองข้อมูลโดยเฉพาะอย่างยิ่งการแนะนำของ GDPR อย่างมีนัยสำคัญสามารถเปลี่ยนแปลงเรื่องของเมื่อข้อมูลควรจะถูกลบอย่างหนัก คุณต้องชั่งน้ำหนักความสมดุลของการไม่ลบข้อมูลที่อาจเป็นประโยชน์ (หรือจำเป็นต้องมีตามกฎหมาย) เพื่อวัตถุประสงค์ในการตรวจสอบในภายหลังโดยไม่จำเป็นต้องเคารพสิทธิของประชาชน (โดยทั่วไปและตามที่กำหนดไว้โดยเฉพาะในกฎหมายที่เกี่ยวข้อง) การออกแบบของคุณ นี่อาจเป็นปัญหากับตารางชั่วคราวของระบบเนื่องจากคุณไม่สามารถแก้ไขประวัติเพื่อล้างข้อมูลส่วนบุคคลโดยไม่มีการเปลี่ยนแปลงสคีมาในระยะสั้นเพื่อปิดการติดตามประวัติในขณะที่คุณทำการเปลี่ยนแปลง


คุณจัดการกับการลบและเปลี่ยนชื่อคอลัมน์อย่างไร ตั้งทุกอย่างเป็นโมฆะ?
Stijn

1
@Stijn: มันไม่บ่อยนักที่โครงสร้างจะเปลี่ยนไปซึ่งก็ไม่ได้เกิดขึ้นมากมายนัก โดยทั่วไป Colunms จะไม่ถูกลบออกเมื่อพวกเขามีอยู่ในการผลิต - หากพวกเขาหยุดการใช้เพียงแค่วางข้อ จำกัด ใด ๆ ที่จะหยุดพวกเขา Benig NULL (หรือเพิ่มค่าเริ่มต้นที่จะจัดการกับข้อ จำกัด โดยใช้ "ค่าวิเศษ" และหยุดการอ้างอิงถึงพวกเขาในรหัสอื่น ๆ สำหรับการเปลี่ยนชื่อ: เพิ่มใหม่หยุดใช้เก่าและคัดลอกข้อมูลจากเก่าไปใหม่ถ้าจำเป็น หากคุณเปลี่ยนชื่อคอลัมน์เพียงตรวจสอบให้แน่ใจว่าการเปลี่ยนแปลงเดียวกันนั้นเกิดขึ้นกับทั้งฐานและตารางตรวจสอบในเวลาเดียวกัน
David Spillett

9

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


7

ฉันจะไปกับตารางแยก Ruby on Rails มีacts_as_versionedปลั๊กอินซึ่งโดยทั่วไปจะบันทึกแถวไปยังตารางอื่นด้วย postfix _versionก่อนที่จะอัปเดต แม้ว่าคุณไม่ต้องการพฤติกรรมที่แน่นอน แต่ก็ควรใช้กับกรณีของคุณ (คัดลอกก่อนที่จะลบ)

เช่นเดียวกับ @Spredzy ฉันขอแนะนำให้เพิ่มdelete_dateคอลัมน์เพื่อให้สามารถล้างระเบียนโดยทางโปรแกรมซึ่งไม่ได้รับการกู้คืนหลังจาก X ชั่วโมง / วัน / อะไรก็ตาม


4

โซลูชันที่เราใช้ภายในสำหรับเรื่องนี้คือการมีคอลัมน์สถานะที่มีค่าฮาร์ดโค้ดสำหรับสถานะเฉพาะของวัตถุบางอย่าง: ลบ, ใช้งาน, ไม่ใช้งาน, เปิด, ปิด, ถูกปิดกั้น - แต่ละสถานะที่มีความหมายบางอย่างที่ใช้ในแอปพลิเคชัน จากมุมมอง db เราไม่ได้ลบวัตถุเราเพียงแค่เปลี่ยนสถานะและเก็บประวัติสำหรับการเปลี่ยนแปลงแต่ละครั้งในตารางวัตถุ


3

เมื่อคุณพูดว่า "โซลูชันหลังจะต้องใช้ตรรกะแอปพลิเคชันเพิ่มเติมเพื่อละเว้นระเบียน" ลบ "


มันไม่ใช่แค่เรื่องของมุมมอง การดำเนินการใด ๆ ที่ดำเนินการกับชุดจะต้องไม่รวมระเบียน "ลบ"
Abie

2

เช่นเดียวกับที่ Spredzy แนะนำเราใช้ฟิลด์การประทับเวลาสำหรับการลบในแอปพลิเคชันทั้งหมดของเรา บูลีนเป็นฟุ่มเฟือยเนื่องจากการตั้งค่าการประทับเวลาบ่งบอกว่าบันทึกถูกลบไปแล้ว ด้วยวิธีนี้ PDO ของเราจะเพิ่มAND (deleted IS NULL OR deleted = 0)ลงในคำสั่งที่เลือกเสมอเว้นเสียแต่ว่าแบบจำลองจะขอบันทึกที่ถูกลบอย่างชัดเจน

ขณะนี้เราไม่ได้รวบรวมขยะบนตารางยกเว้นที่มี blobs หรือข้อความ พื้นที่ว่างเล็กน้อยหากบันทึกเป็นปกติดีและการจัดทำดัชนีdeletedฟิลด์ทำให้มีผลกระทบ จำกัด ในความเร็วที่เลือก


0

คุณอาจวางความรับผิดชอบไว้ที่ผู้ใช้ (และนักพัฒนา) และไปกับลำดับของ 'คุณแน่ใจหรือไม่', 'คุณแน่ใจหรือไม่?' และ 'คุณมีความผิดอย่างดีและแน่นอนจริง ๆ ?' คำถามก่อนที่จะถูกลบบันทึก พิจารณาอย่างรอบคอบ แต่ก็คุ้มค่า


0

ฉันเคยเห็นแถวของตารางที่มีคอลัมน์เช่น 'ลบวันที่' ในพวกเขาและฉันไม่ชอบพวกเขา ความคิดของ 'ลบ' คือรายการไม่ควรทำตั้งแต่แรก ในทางปฏิบัติแล้วพวกเขาไม่สามารถลบออกจากฐานข้อมูล แต่ฉันไม่ต้องการให้พวกเขาด้วยข้อมูลที่ร้อนแรงของฉัน แถวที่ถูกลบแบบลอจิคัลคือข้อมูลที่เป็นหวัดถ้าไม่มีใครต้องการเห็นข้อมูลที่ถูกลบโดยเฉพาะ

นอกจากนี้ทุกแบบสอบถามที่เขียนจะต้องแยกพวกเขาและดัชนีจะต้องพิจารณาด้วยเช่นกัน

สิ่งที่ฉันต้องการจะดูคือการเปลี่ยนแปลงในระดับสถาปัตยกรรมฐานข้อมูลและระดับแอปพลิเคชัน: สร้างสคีมาที่เรียกว่า 'ลบ' แต่ละตารางที่ผู้ใช้กำหนดมีความเท่าเทียมกันใน schema 'ลบ' พร้อมกับเขตข้อมูลพิเศษที่ถือข้อมูลเมตา - ผู้ใช้ที่ลบมันและเมื่อ ต้องสร้างคีย์ต่างประเทศ

ถัดไปการลบจะเป็นการแทรกการลบ ก่อนอื่นแถวที่จะลบจะถูกแทรกลงในสคีมา 'ลบ' แถวที่มีปัญหาในตารางหลักนั้นจะสามารถลบได้ อย่างไรก็ตามต้องเพิ่มตรรกะพิเศษในที่ใดที่หนึ่งตามแนว การละเมิดคีย์ต่างประเทศสามารถจัดการได้

ต้องจัดการกับกุญแจต่างประเทศอย่างถูกต้อง เป็นวิธีปฏิบัติที่ไม่ถูกต้องที่จะลบแถวอย่างมีเหตุผล แต่มีหลัก / ไม่ซ้ำกันมีคอลัมน์ในตารางอื่น ๆ ที่อ้างถึง สิ่งนี้ไม่ควรเกิดขึ้น งานปกติสามารถลบแถวแม่ม่าย (แถวที่มีคีย์หลักไม่มีการอ้างอิงในตารางอื่นแม้จะมี foreign key อยู่) นี่คือตรรกะทางธุรกิจ

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

ข้อเสียเปรียบหลักคือขนาดของการดำเนินการ ขณะนี้มีสองการดำเนินการแทนการดำเนินการเช่นเดียวกับตรรกะพิเศษและการจัดการข้อผิดพลาด มันสามารถนำไปสู่การล็อคมากกว่าการปรับปรุงคอลัมน์เดียวมิฉะนั้นจะใช้ การทำธุรกรรมถือล็อคในตารางอีกต่อไปและมีสองตารางที่เกี่ยวข้อง การลบข้อมูลการผลิตอย่างน้อยในประสบการณ์ของฉันเป็นสิ่งที่ทำไม่ค่อย แม้จะยังอยู่ในหนึ่งในตารางหลัก 7.5% ของเกือบ 100 ล้านรายการมีรายการในคอลัมน์ 'DeletedDate'

ในฐานะที่เป็นคำตอบของคำถามแอปพลิเคชันจะต้องระวัง 'ยกเลิกการลบของ มันจะต้องทำแบบเดียวกันในลำดับย้อนกลับ: แทรกแถวจากสคีมา 'ลบ' ลงในตารางหลักแล้วลบแถวออกจากสคีมาที่ถูกลบ ต้องมีการจัดการตรรกะและข้อผิดพลาดเพิ่มเติมเพื่อหลีกเลี่ยงข้อผิดพลาดปัญหาเกี่ยวกับกุญแจต่างประเทศและสิ่งที่คล้าย

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