วิธีควบคุมเวอร์ชันของเร็กคอร์ดในฐานข้อมูล


177

สมมติว่าฉันมีบันทึกในฐานข้อมูลและทั้งผู้ดูแลระบบและผู้ใช้ปกติสามารถทำการอัปเดตได้

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

คำตอบ:


164

สมมติว่าคุณมีFOOตารางที่ผู้ดูแลระบบและผู้ใช้สามารถอัปเดต เวลาส่วนใหญ่คุณสามารถเขียนคิวรีกับตาราง FOO วันแห่งความสุข.

จากนั้นฉันจะสร้างFOO_HISTORYตาราง นี่คือคอลัมน์ทั้งหมดของFOOตาราง คีย์หลักเหมือนกับ FOO บวกคอลัมน์ RevisionNumber มีความสำคัญจากต่างประเทศเป็นไปFOO_HISTORY FOOคุณอาจเพิ่มคอลัมน์ที่เกี่ยวข้องกับการแก้ไขเช่น UserId และ RevisionDate เติม RevisionNumber ในแบบที่เพิ่มมากขึ้นตลอดทุก*_HISTORYตาราง (เช่นจากลำดับของ Oracle หรือเทียบเท่า) อย่าพึ่งมีเพียงการเปลี่ยนแปลงเพียงครั้งเดียวในหนึ่งวินาที (เช่นอย่าใส่RevisionDateคีย์หลัก)

ตอนนี้ทุกครั้งที่คุณปรับปรุงเพียงแค่ก่อนที่จะทำการปรับปรุงคุณใส่ค่าเก่าเข้าFOO FOO_HISTORYคุณทำสิ่งนี้ในระดับพื้นฐานในการออกแบบของคุณเพื่อให้โปรแกรมเมอร์ไม่สามารถพลาดขั้นตอนนี้โดยไม่ได้ตั้งใจ

หากคุณต้องการลบแถวออกจากFOOคุณมีตัวเลือกบางอย่าง เรียงซ้อนและลบประวัติทั้งหมดหรือทำการลบแบบโลจิคัลโดยการตั้งค่าสถานะFOOเป็นลบ

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


1
คุณสามารถทำการปรับปรุงตารางการตรวจสอบด้วยทริกเกอร์ฐานข้อมูลหากชั้นการเข้าถึงข้อมูลของคุณไม่สนับสนุนโดยตรง นอกจากนี้ยังไม่ยากที่จะสร้างตัวสร้างโค้ดเพื่อสร้างทริกเกอร์ที่ใช้การวิปัสสนาจากพจนานุกรมข้อมูลระบบ
ConcOfOfTunbridgeWells

44
ฉัน woyuld แนะนำให้คุณแทรกข้อมูลใหม่ไม่ใช่ก่อนหน้าดังนั้นตารางประวัติจึงมีข้อมูลทั้งหมด แม้ว่าจะจัดเก็บข้อมูลซ้ำซ้อน แต่ก็กำจัดกรณีพิเศษที่จำเป็นในการจัดการกับการค้นหาในทั้งสองตารางเมื่อจำเป็นต้องใช้ข้อมูลประวัติ
Nerdfest

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

3
@Hydrargyrum ตารางที่ถือค่าปัจจุบันจะทำงานได้ดีกว่ามุมมองของตารางประวัติศาสตร์ คุณอาจต้องการกำหนดคีย์ต่างประเทศที่อ้างอิงถึงค่าปัจจุบัน
WW

2
There is a foreign key from FOO_HISTORY to FOO': ความคิดที่ไม่ดีฉันต้องการลบบันทึกจาก foo โดยไม่เปลี่ยนประวัติ ตารางประวัติควรแทรกอย่างเดียวในการใช้งานปกติ
Jasen

46

ฉันคิดว่าคุณกำลังมองหาเวอร์ชันของเนื้อหาของฐานข้อมูล (เช่น StackOverflow ทำเมื่อมีคนแก้ไขคำถาม / คำตอบ) จุดเริ่มต้นที่ดีอาจดูที่ฐานข้อมูลบางรุ่นที่ใช้การติดตามการแก้ไข

ตัวอย่างที่ดีที่สุดที่นึกถึงคือ MediaWiki เอ็นจิ้น Wikipedia เปรียบเทียบไดอะแกรมฐานข้อมูลที่นี่โดยเฉพาะตารางการแก้ไข

ขึ้นอยู่กับเทคโนโลยีที่คุณใช้คุณจะต้องค้นหาอัลกอริทึม diff / merge ที่ดี

ตรวจสอบคำถามนี้หากใช้กับ. NET


30

ในโลก BI คุณสามารถทำได้โดยเพิ่ม startDate และ endDate ลงในตารางที่คุณต้องการให้เป็นเวอร์ชั่น เมื่อคุณแทรกระเบียนแรกลงในตาราง startDate จะบรรจุ แต่ endDate เป็นโมฆะ เมื่อคุณแทรกระเบียนที่สองคุณยังปรับปรุง endDate ของระเบียนแรกด้วย startDate ของระเบียนที่สอง

เมื่อคุณต้องการดูระเบียนปัจจุบันคุณเลือกระเบียนที่ endDate เป็นโมฆะ

นี้บางครั้งเรียกว่าชนิดที่ 2 ช้าเปลี่ยนขนาด ดูเพิ่มเติมที่TupleVersioning


ตารางของฉันจะไม่ใหญ่ขึ้นโดยใช้วิธีนี้หรือไม่
Niels Bosma

1
ใช่ แต่คุณสามารถจัดการกับสิ่งนั้นได้โดยทำดัชนีและ / หรือแบ่งพาร์ติชันตาราง นอกจากนี้จะมีเพียงไม่กี่โต๊ะขนาดใหญ่ ส่วนใหญ่จะเล็กกว่ามาก
ConcOfOfTunbridgeWells

หากฉันไม่เข้าใจผิดความผิดพลาดเพียงอย่างเดียวที่นี่คือมัน จำกัด การเปลี่ยนแปลงเพียงครั้งเดียวต่อวินาทีที่ถูกต้อง?
จ่ายเงินคืน

@pimbrouwers ใช่แล้วท้ายที่สุดมันก็ขึ้นอยู่กับความแม่นยำของฟิลด์และฟังก์ชั่นที่เติมเข้าไป
Dave Neeley

9

อัปเกรดเป็น SQL 2008

ลองใช้การติดตามการเปลี่ยนแปลงของ SQL ใน SQL 2008 แทนที่จะเป็นการแฮ็กคอลัมน์และหลุมฝังศพคุณสามารถใช้คุณลักษณะใหม่นี้สำหรับการติดตามการเปลี่ยนแปลงข้อมูลในฐานข้อมูลของคุณ

การติดตามการเปลี่ยนแปลง MSDN SQL 2008


7

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


6

สองตัวเลือก:

  1. มีตารางประวัติ - แทรกข้อมูลเก่าลงในตารางประวัตินี้ทุกครั้งที่มีการอัปเดตต้นฉบับ
  2. ตารางการตรวจสอบ - เก็บค่าก่อนและหลังค่า - สำหรับคอลัมน์ที่แก้ไขในตารางการตรวจสอบพร้อมกับข้อมูลอื่น ๆ เช่นใครปรับปรุงและเมื่อใด

5

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

โบนัสเพิ่มเติมของวิธีนี้คือการตรวจสอบจะเกิดขึ้นไม่ว่าการดำเนินการ sql จะเกิดขึ้นผ่านการเข้าถึงข้อมูลของคุณหรือผ่านคิวรี่ SQL แบบแมนนวล; (ตามการตรวจสอบจะดำเนินการบนเซิร์ฟเวอร์เอง)


3

คุณไม่ได้พูดว่าฐานข้อมูลใดและฉันไม่เห็นมันในแท็กโพสต์ หากเป็นของ Oracle ฉันสามารถแนะนำวิธีการที่สร้างขึ้นใน Designer: ใช้ตารางเจอร์นัล ถ้าเป็นฐานข้อมูลอื่นฉันก็แนะนำวิธีเดียวกันโดยพื้นฐานเช่นกัน ...

วิธีการทำงานในกรณีที่คุณต้องการที่จะทำซ้ำในฐานข้อมูลอื่นหรือบางทีถ้าคุณเพียงแค่ต้องการที่จะเข้าใจมันก็คือสำหรับตารางที่มีตารางเงาที่สร้างขึ้นเช่นกันเพียงตารางฐานข้อมูลปกติที่มีรายละเอียดของเขตข้อมูลเดียวกัน รวมทั้งฟิลด์พิเศษบางอย่างเช่นการดำเนินการล่าสุด (สตริงค่าทั่วไป "INS" สำหรับการแทรก "UPD" สำหรับการอัปเดตและ "DEL" สำหรับการลบ) อายุการใช้งานเมื่อการกระทำเกิดขึ้นและ ID ผู้ใช้สำหรับผู้ที่ทำ มัน.

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

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


2

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

วิธีการทำงานก็คือเมื่อนักเรียนทำบทเรียนแล้วผลลัพธ์ของพวกเขาจะถูกแนบกับเวอร์ชันที่นักเรียนทำเสร็จ หากมีการเปลี่ยนแปลงผลลัพธ์ของคะแนนจะเป็นเวอร์ชันของพวกเขาเสมอ

ด้วยวิธีนี้หากมีการลบหรือย้ายบทเรียนบทเรียนผลลัพธ์ของพวกเขาจะไม่เปลี่ยนแปลง

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

สิ่งหนึ่งที่ฉันอาจเปลี่ยนแปลงเมื่อฉันทดสอบมันคือฉันอาจใช้แนวคิด endDate null ที่กล่าวถึงก่อนหน้านี้ ในระบบของฉันเพื่อหาเวอร์ชั่นใหม่ล่าสุดฉันจะต้องค้นหา max (id) ระบบอื่นมองหา endDate = null ไม่แน่ใจว่าผลประโยชน์นอกเขตมีวันที่อื่น

สองเซ็นต์ของฉัน


2

ในขณะที่ @WW answer เป็นคำตอบที่ดีอีกวิธีหนึ่งคือสร้างคอลัมน์เวอร์ชันและเก็บเวอร์ชันทั้งหมดของคุณไว้ในตารางเดียวกัน

สำหรับวิธีการหนึ่งตารางคุณอาจ:

  • ใช้การตั้งค่าสถานะเพื่อระบุ ala Word Pressล่าสุด
  • outer joinหรือทำมากขึ้นกว่ารุ่นที่น่ารังเกียจ

ตัวอย่าง SQL ของouter joinวิธีการใช้หมายเลขการแก้ไขคือ:

SELECT tc.*
FROM text_content tc
LEFT OUTER JOIN text_content mc ON tc.path = mc.path
AND mc.revision > tc.revision
WHERE mc.revision is NULL 
AND tc.path = '/stuff' -- path in this case is our natural id.

ข่าวดีก็คือข้างต้นจะต้องมีการรวมouter joinและด้านนอกได้ช้า ข่าวดีก็คือว่าการสร้างรายการใหม่นั้นมีราคาถูกกว่าในทางทฤษฎีเพราะคุณสามารถทำได้ในการเขียนหนึ่งครั้งโดยไม่มีธุรกรรม (สมมติว่าฐานข้อมูลของคุณเป็นแบบอะตอม)

ตัวอย่างการแก้ไขใหม่สำหรับ'/stuff'อาจเป็น:

INSERT INTO text_content (id, path, data, revision, revision_comment, enabled, create_time, update_time)
(
SELECT
(md5(random()::text)) -- {id}
, tc.path
, 'NEW' -- {data}
, (tc.revision + 1)
, 'UPDATE' -- {comment}
, 't' -- {enabled}
, tc.create_time
, now() 
FROM text_content tc
LEFT OUTER JOIN text_content mc ON tc.path = mc.path
AND mc.revision > tc.revision
WHERE mc.revision is NULL 
AND tc.path = '/stuff' -- {path}
)

เราแทรกโดยใช้ข้อมูลเก่า สิ่งนี้มีประโยชน์อย่างยิ่งหากคุณต้องการอัปเดตคอลัมน์เดียวและหลีกเลี่ยงการล็อกในแง่ดีและหรือธุรกรรม

วิธีการตั้งค่าสถานะและวิธีตารางประวัติต้องการให้แทรก / ปรับปรุงสองแถว

ข้อได้เปรียบอื่น ๆ ด้วยวิธีouter joinการแก้ไขหมายเลขคือคุณสามารถ refactor วิธีหลายตารางในภายหลังด้วยทริกเกอร์ได้เนื่องจากทริกเกอร์ของคุณควรทำสิ่งต่าง ๆ ดังกล่าวข้างต้น


2

Alok แนะนำAudit tableข้างต้นฉันต้องการอธิบายในโพสต์ของฉัน

ฉันยอมรับการออกแบบตารางเดียวแบบไม่ใช้สคีมาในโครงการของฉัน

schema:

  • id - การรวมอัตโนมัติเข้าด้วยกัน
  • ชื่อผู้ใช้ - STRING
  • tablename - STRING
  • oldvalue - TEXT / JSON
  • ค่าใหม่ - TEXT / JSON
  • สร้างเมื่อ - DATETIME

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

ข้อดีกับการออกแบบนี้:

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

ข้อเสียกับการออกแบบนี้:

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