สมมติว่าฉันมีบันทึกในฐานข้อมูลและทั้งผู้ดูแลระบบและผู้ใช้ปกติสามารถทำการอัปเดตได้
ทุกคนสามารถแนะนำวิธีการ / สถาปัตยกรรมที่ดีวิธีควบคุมเวอร์ชันทุกการเปลี่ยนแปลงในตารางนี้เพื่อให้สามารถย้อนกลับระเบียนไปยังการแก้ไขก่อนหน้าได้
สมมติว่าฉันมีบันทึกในฐานข้อมูลและทั้งผู้ดูแลระบบและผู้ใช้ปกติสามารถทำการอัปเดตได้
ทุกคนสามารถแนะนำวิธีการ / สถาปัตยกรรมที่ดีวิธีควบคุมเวอร์ชันทุกการเปลี่ยนแปลงในตารางนี้เพื่อให้สามารถย้อนกลับระเบียนไปยังการแก้ไขก่อนหน้าได้
คำตอบ:
สมมติว่าคุณมีFOO
ตารางที่ผู้ดูแลระบบและผู้ใช้สามารถอัปเดต เวลาส่วนใหญ่คุณสามารถเขียนคิวรีกับตาราง FOO วันแห่งความสุข.
จากนั้นฉันจะสร้างFOO_HISTORY
ตาราง นี่คือคอลัมน์ทั้งหมดของFOO
ตาราง คีย์หลักเหมือนกับ FOO บวกคอลัมน์ RevisionNumber มีความสำคัญจากต่างประเทศเป็นไปFOO_HISTORY
FOO
คุณอาจเพิ่มคอลัมน์ที่เกี่ยวข้องกับการแก้ไขเช่น UserId และ RevisionDate เติม RevisionNumber ในแบบที่เพิ่มมากขึ้นตลอดทุก*_HISTORY
ตาราง (เช่นจากลำดับของ Oracle หรือเทียบเท่า) อย่าพึ่งมีเพียงการเปลี่ยนแปลงเพียงครั้งเดียวในหนึ่งวินาที (เช่นอย่าใส่RevisionDate
คีย์หลัก)
ตอนนี้ทุกครั้งที่คุณปรับปรุงเพียงแค่ก่อนที่จะทำการปรับปรุงคุณใส่ค่าเก่าเข้าFOO
FOO_HISTORY
คุณทำสิ่งนี้ในระดับพื้นฐานในการออกแบบของคุณเพื่อให้โปรแกรมเมอร์ไม่สามารถพลาดขั้นตอนนี้โดยไม่ได้ตั้งใจ
หากคุณต้องการลบแถวออกจากFOO
คุณมีตัวเลือกบางอย่าง เรียงซ้อนและลบประวัติทั้งหมดหรือทำการลบแบบโลจิคัลโดยการตั้งค่าสถานะFOO
เป็นลบ
วิธีนี้เป็นวิธีที่ดีเมื่อคุณสนใจค่านิยมในปัจจุบันและมีเพียงบางครั้งในประวัติศาสตร์ หากคุณต้องการประวัติเสมอคุณสามารถใส่วันที่เริ่มต้นและสิ้นสุดที่มีประสิทธิภาพและเก็บบันทึกทั้งหมดไว้ในFOO
ตัวเอง ทุกคำถามจะต้องตรวจสอบวันที่เหล่านั้น
There is a foreign key from FOO_HISTORY to FOO'
: ความคิดที่ไม่ดีฉันต้องการลบบันทึกจาก foo โดยไม่เปลี่ยนประวัติ ตารางประวัติควรแทรกอย่างเดียวในการใช้งานปกติ
ฉันคิดว่าคุณกำลังมองหาเวอร์ชันของเนื้อหาของฐานข้อมูล (เช่น StackOverflow ทำเมื่อมีคนแก้ไขคำถาม / คำตอบ) จุดเริ่มต้นที่ดีอาจดูที่ฐานข้อมูลบางรุ่นที่ใช้การติดตามการแก้ไข
ตัวอย่างที่ดีที่สุดที่นึกถึงคือ MediaWiki เอ็นจิ้น Wikipedia เปรียบเทียบไดอะแกรมฐานข้อมูลที่นี่โดยเฉพาะตารางการแก้ไข
ขึ้นอยู่กับเทคโนโลยีที่คุณใช้คุณจะต้องค้นหาอัลกอริทึม diff / merge ที่ดี
ตรวจสอบคำถามนี้หากใช้กับ. NET
ในโลก BI คุณสามารถทำได้โดยเพิ่ม startDate และ endDate ลงในตารางที่คุณต้องการให้เป็นเวอร์ชั่น เมื่อคุณแทรกระเบียนแรกลงในตาราง startDate จะบรรจุ แต่ endDate เป็นโมฆะ เมื่อคุณแทรกระเบียนที่สองคุณยังปรับปรุง endDate ของระเบียนแรกด้วย startDate ของระเบียนที่สอง
เมื่อคุณต้องการดูระเบียนปัจจุบันคุณเลือกระเบียนที่ endDate เป็นโมฆะ
นี้บางครั้งเรียกว่าชนิดที่ 2 ช้าเปลี่ยนขนาด ดูเพิ่มเติมที่TupleVersioning
อัปเกรดเป็น SQL 2008
ลองใช้การติดตามการเปลี่ยนแปลงของ SQL ใน SQL 2008 แทนที่จะเป็นการแฮ็กคอลัมน์และหลุมฝังศพคุณสามารถใช้คุณลักษณะใหม่นี้สำหรับการติดตามการเปลี่ยนแปลงข้อมูลในฐานข้อมูลของคุณ
แค่อยากจะเพิ่มว่าหนึ่งในทางออกที่ดีในการแก้ไขปัญหานี้คือการใช้ฐานข้อมูลชั่วคราว ผู้ค้าฐานข้อมูลจำนวนมากเสนอคุณสมบัตินี้ไม่ว่าจะอยู่ในกล่องหรือผ่านทางส่วนขยาย ฉันใช้ส่วนขยายตารางชั่วคราวกับ PostgreSQL ได้สำเร็จแต่คนอื่นก็มีเช่นกัน เมื่อใดก็ตามที่คุณอัปเดตระเบียนในฐานข้อมูลฐานข้อมูลจะเก็บรักษาบันทึกเวอร์ชันนั้นไว้เช่นกัน
สองตัวเลือก:
คุณสามารถทำการตรวจสอบในตาราง SQL ผ่านทางทริกเกอร์ SQL จากทริกเกอร์คุณสามารถเข้าถึง 2 ตารางพิเศษ ( แทรกและลบ ) ตารางเหล่านี้มีแถวที่แน่นอนที่ถูกแทรกหรือลบทุกครั้งที่มีการปรับปรุงตาราง ในทริกเกอร์ SQL คุณสามารถนำแถวที่ได้รับการดัดแปลงแล้วใส่เข้าไปในตารางการตรวจสอบ วิธีการนี้หมายความว่าการตรวจสอบของคุณมีความโปร่งใสต่อโปรแกรมเมอร์ ไม่ต้องใช้ความพยายามจากพวกเขาหรือความรู้การใช้งานใด ๆ
โบนัสเพิ่มเติมของวิธีนี้คือการตรวจสอบจะเกิดขึ้นไม่ว่าการดำเนินการ sql จะเกิดขึ้นผ่านการเข้าถึงข้อมูลของคุณหรือผ่านคิวรี่ SQL แบบแมนนวล; (ตามการตรวจสอบจะดำเนินการบนเซิร์ฟเวอร์เอง)
คุณไม่ได้พูดว่าฐานข้อมูลใดและฉันไม่เห็นมันในแท็กโพสต์ หากเป็นของ Oracle ฉันสามารถแนะนำวิธีการที่สร้างขึ้นใน Designer: ใช้ตารางเจอร์นัล ถ้าเป็นฐานข้อมูลอื่นฉันก็แนะนำวิธีเดียวกันโดยพื้นฐานเช่นกัน ...
วิธีการทำงานในกรณีที่คุณต้องการที่จะทำซ้ำในฐานข้อมูลอื่นหรือบางทีถ้าคุณเพียงแค่ต้องการที่จะเข้าใจมันก็คือสำหรับตารางที่มีตารางเงาที่สร้างขึ้นเช่นกันเพียงตารางฐานข้อมูลปกติที่มีรายละเอียดของเขตข้อมูลเดียวกัน รวมทั้งฟิลด์พิเศษบางอย่างเช่นการดำเนินการล่าสุด (สตริงค่าทั่วไป "INS" สำหรับการแทรก "UPD" สำหรับการอัปเดตและ "DEL" สำหรับการลบ) อายุการใช้งานเมื่อการกระทำเกิดขึ้นและ ID ผู้ใช้สำหรับผู้ที่ทำ มัน.
ผ่านทริกเกอร์ทุกการกระทำไปยังแถวใด ๆ ในตารางจะแทรกแถวใหม่ในตารางวารสารด้วยค่าใหม่การกระทำใดที่กระทำเมื่อใดและโดยผู้ใช้ใด คุณไม่ได้ลบแถวใด ๆ (อย่างน้อยก็ไม่ใช่ในช่วงสองสามเดือนที่ผ่านมา) ใช่มันจะเติบโตใหญ่แถวง่าย ๆ นับล้าน แต่คุณสามารถติดตามค่าของระเบียนใด ๆณ เวลาใด ๆนับตั้งแต่การเริ่มต้นการบันทึกหรือแถววารสารเก่าถูกกำจัดครั้งสุดท้ายและใครเป็นผู้เปลี่ยนแปลงครั้งล่าสุด
ใน Oracle ทุกสิ่งที่คุณต้องการจะถูกสร้างขึ้นโดยอัตโนมัติในรูปของรหัส SQL สิ่งที่คุณต้องทำก็คือการคอมไพล์ / รันมัน และมันมาพร้อมกับแอปพลิเคชั่น CRUD ขั้นพื้นฐาน (จริง ๆ แล้วคือ "R" เท่านั้น) เพื่อตรวจสอบ
ฉันก็กำลังทำสิ่งเดียวกัน ฉันกำลังสร้างฐานข้อมูลสำหรับแผนการสอน แผนเหล่านี้ต้องการความยืดหยุ่นในการกำหนดเวอร์ชันของอะตอมเปลี่ยน กล่าวอีกนัยหนึ่งการเปลี่ยนแปลงแต่ละครั้งไม่ว่าจะเล็กเพียงใดกับแผนการสอนจะต้องได้รับอนุญาต แต่ต้องมีการรักษาไว้ด้วยเช่นกัน ด้วยวิธีนี้ผู้สร้างบทเรียนสามารถแก้ไขแผนการสอนขณะที่นักเรียนใช้งาน
วิธีการทำงานก็คือเมื่อนักเรียนทำบทเรียนแล้วผลลัพธ์ของพวกเขาจะถูกแนบกับเวอร์ชันที่นักเรียนทำเสร็จ หากมีการเปลี่ยนแปลงผลลัพธ์ของคะแนนจะเป็นเวอร์ชันของพวกเขาเสมอ
ด้วยวิธีนี้หากมีการลบหรือย้ายบทเรียนบทเรียนผลลัพธ์ของพวกเขาจะไม่เปลี่ยนแปลง
วิธีที่ฉันทำอยู่ตอนนี้คือการจัดการข้อมูลทั้งหมดในตารางเดียว โดยปกติฉันจะมีเพียงหนึ่งช่อง id แต่ด้วยระบบนี้ฉันใช้ id และ sub_id sub_id จะอยู่กับแถวตลอดเวลาผ่านการอัปเดตและลบ ID นั้นถูกเพิ่มโดยอัตโนมัติ ซอฟต์แวร์แผนการสอนจะเชื่อมโยงไปยัง sub_id ใหม่ล่าสุด ผลลัพธ์ของนักเรียนจะเชื่อมโยงกับรหัส ฉันยังได้รวมการประทับเวลาสำหรับการติดตามเมื่อมีการเปลี่ยนแปลงเกิดขึ้น แต่ไม่จำเป็นต้องจัดการกับการกำหนดเวอร์ชัน
สิ่งหนึ่งที่ฉันอาจเปลี่ยนแปลงเมื่อฉันทดสอบมันคือฉันอาจใช้แนวคิด endDate null ที่กล่าวถึงก่อนหน้านี้ ในระบบของฉันเพื่อหาเวอร์ชั่นใหม่ล่าสุดฉันจะต้องค้นหา max (id) ระบบอื่นมองหา endDate = null ไม่แน่ใจว่าผลประโยชน์นอกเขตมีวันที่อื่น
สองเซ็นต์ของฉัน
ในขณะที่ @WW answer เป็นคำตอบที่ดีอีกวิธีหนึ่งคือสร้างคอลัมน์เวอร์ชันและเก็บเวอร์ชันทั้งหมดของคุณไว้ในตารางเดียวกัน
สำหรับวิธีการหนึ่งตารางคุณอาจ:
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 วิธีหลายตารางในภายหลังด้วยทริกเกอร์ได้เนื่องจากทริกเกอร์ของคุณควรทำสิ่งต่าง ๆ ดังกล่าวข้างต้น
Alok แนะนำAudit table
ข้างต้นฉันต้องการอธิบายในโพสต์ของฉัน
ฉันยอมรับการออกแบบตารางเดียวแบบไม่ใช้สคีมาในโครงการของฉัน
schema:
ตารางนี้สามารถเก็บบันทึกประวัติสำหรับแต่ละตารางทั้งหมดในที่เดียวด้วยประวัติวัตถุที่สมบูรณ์ในบันทึกเดียว ตารางนี้สามารถเติมข้อมูลโดยใช้ทริกเกอร์ / ตะขอที่มีการเปลี่ยนแปลงข้อมูลจัดเก็บสแนปชอตค่าเก่าและใหม่ของแถวเป้าหมาย
ข้อดีกับการออกแบบนี้:
ข้อเสียกับการออกแบบนี้: