วิธีเก็บข้อมูลทางประวัติศาสตร์


162

เพื่อนร่วมงานบางคนกับฉันได้ถกเถียงกันถึงวิธีที่ดีที่สุดในการเก็บข้อมูลทางประวัติศาสตร์ ปัจจุบันสำหรับบางระบบฉันใช้ตารางแยกเพื่อเก็บข้อมูลประวัติและฉันเก็บตารางดั้งเดิมสำหรับบันทึกปัจจุบันที่ใช้งานอยู่ สมมุติว่าฉันมีตาราง FOO ภายใต้ระบบของฉันระเบียนที่ใช้งานอยู่ทั้งหมดจะอยู่ใน FOO และระเบียนที่ผ่านมาทั้งหมดจะอยู่ใน FOO_Hist ผู้ใช้สามารถอัปเดตฟิลด์ต่าง ๆ มากมายใน FOO ได้ดังนั้นฉันต้องการรักษาบัญชีที่ถูกต้องของทุกสิ่งที่อัปเดต FOO_Hist เก็บข้อมูลในฟิลด์เดียวกับ FOO ยกเว้นการเพิ่ม HIST_ID โดยอัตโนมัติ เวลา FOO มีการปรับปรุงทุกฉันดำเนินการคำสั่งแทรกเข้าไปใน FOO_Hist insert into FOO_HIST select * from FOO where id = @idคล้ายกับ:

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

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

คุณหรือ บริษัท ของคุณจัดการกับสิ่งนี้อย่างไร

ฉันใช้ MS SQL Server 2008 แต่ฉันต้องการที่จะเก็บคำตอบทั่วไปและโดยพลการของ DBMS ใด ๆ

คำตอบ:


80

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

หากคุณดูอย่างใกล้ชิดข้อกำหนดส่วนใหญ่สำหรับข้อมูลประวัติจะอยู่ในหมวดหมู่หนึ่งในสองประเภท:

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

  • การรายงานประวัติ: การรายงานสถานะทางประวัติศาสตร์ตำแหน่ง 'ตามที่' หรือการรายงานเชิงวิเคราะห์เมื่อเวลาผ่านไป อาจเป็นไปได้ที่จะปฏิบัติตามข้อกำหนดการรายงานทางประวัติศาสตร์อย่างง่ายโดยการปิดตารางการบันทึกการตรวจสอบของการเรียงลำดับที่อธิบายข้างต้น หากคุณมีข้อกำหนดที่ซับซ้อนมากขึ้นการประหยัด data ในการรายงานอาจทำได้มากกว่าการลองและรวมประวัติเข้ากับระบบปฏิบัติการโดยตรง

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

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


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

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

ใช่ฉันเห็นด้วย 100% ขอบคุณ.
แอรอน

40

ฉันไม่คิดว่าจะมีวิธีมาตรฐานแบบเฉพาะเจาะจง แต่ฉันคิดว่าฉันจะใช้วิธีที่เป็นไปได้ ฉันทำงานใน Oracle และกรอบงานเว็บแอปพลิเคชันภายในของเราซึ่งใช้ XML สำหรับเก็บข้อมูลแอปพลิเคชัน

เราใช้สิ่งที่เรียกว่าแบบจำลองต้นแบบ - รายละเอียดที่ง่ายที่สุดประกอบด้วย:

ตัวอย่างตารางหลักที่เรียกว่าWidgetsมักจะมี ID มักจะมีข้อมูลที่จะไม่เปลี่ยนแปลงตลอดเวลา / ไม่ใช่ประวัติ

ตารางรายละเอียด / ประวัติเช่นเรียกว่าWidget_Detailsมีอย่างน้อย:

  • ID - คีย์หลัก รายละเอียด / รหัสประวัติ
  • MASTER_ID - ตัวอย่างเช่นในกรณีนี้เรียกว่า 'WIDGET_ID' นี่คือ FK สำหรับบันทึกต้นแบบ
  • START_DATETIME - เวลาประทับที่ระบุจุดเริ่มต้นของแถวฐานข้อมูลนั้น
  • END_DATETIME - เวลาประทับที่ระบุในตอนท้ายของแถวฐานข้อมูลนั้น
  • STATUS_CONTROL - คอลัมน์ถ่านเดียวระบุสถานะของแถว 'C' หมายถึงปัจจุบัน NULL หรือ 'A' จะเป็นประวัติ / เก็บถาวร เราใช้สิ่งนี้เพียงเพราะเราไม่สามารถจัดทำดัชนีเมื่อ END_DATETIME เป็น NULL
  • CREATED_BY_WUA_ID - เก็บ ID ของบัญชีที่ทำให้แถวถูกสร้างขึ้น
  • XMLDATA - เก็บข้อมูลจริง

เอนทิตีเริ่มต้นด้วยการมี 1 แถวในต้นแบบและ 1 แถวในรายละเอียด รายละเอียดมีวันที่สิ้นสุด NULL และ STATUS_CONTROL เป็น 'C' เมื่อมีการอัปเดตแถวปัจจุบันจะถูกอัปเดตเป็น END_DATETIME ของเวลาปัจจุบันและ status_control ถูกตั้งค่าเป็น NULL (หรือ 'A' หากต้องการ) แถวใหม่ถูกสร้างขึ้นในตารางรายละเอียดซึ่งยังคงเชื่อมโยงกับต้นแบบเดียวกันโดยมี status_control 'C', id ของบุคคลที่ทำการอัปเดตและข้อมูลใหม่ที่เก็บไว้ในคอลัมน์ XMLDATA

นี่คือพื้นฐานของแบบจำลองประวัติศาสตร์ของเรา ตรรกะการสร้าง / อัปเดตได้รับการจัดการในแพ็คเกจ Oracle PL / SQL เพื่อให้คุณสามารถส่งผ่านฟังก์ชัน ID ปัจจุบัน ID ผู้ใช้ของคุณและข้อมูล XML ใหม่และภายในจะทำการอัปเดต / แทรกแถวเพื่อเป็นตัวแทนในรูปแบบเชิงประวัติ . เวลาเริ่มต้นและสิ้นสุดแสดงว่าแถวนั้นในตารางใช้งานได้เมื่อใด

พื้นที่เก็บข้อมูลราคาถูกโดยทั่วไปเราไม่ลบข้อมูลและต้องการเก็บหลักฐานการตรวจสอบ สิ่งนี้ทำให้เราเห็นว่าข้อมูลของเราเป็นอย่างไรในเวลาใดก็ตาม ด้วยการทำดัชนี status_control = 'C' หรือใช้มุมมองการเรียงกันไม่เป็นปัญหา เห็นได้ชัดว่าคำสั่งของคุณต้องคำนึงถึงคุณควรใช้รุ่นปัจจุบัน (NULL end_datetime และ status_control = 'C') รุ่นของบันทึก


สวัสดีคริสถ้าคุณทำเช่นนั้น ID (คีย์หลัก) จะต้องเปลี่ยนใช่มั้ย ความสัมพันธ์กับโต๊ะอื่นถ้ามันใช้กับตารางอื่น?
projo

@projo ID บนโต๊ะหลักของคุณคือ PK และตามแนวคิด "PK" สำหรับแนวคิดใดก็ตามที่คุณกำลังติดต่อด้วย ID บนตารางรายละเอียดคือ PK เพื่อระบุเวอร์ชันเชิงประวัติสำหรับต้นแบบ (ซึ่งเป็นอีกคอลัมน์หนึ่งของรายละเอียด) เมื่อสร้างความสัมพันธ์คุณมักจะอ้างอิง PK ที่แท้จริงของแนวคิดของคุณ (เช่น ID ในตารางต้นแบบหรือคอลัมน์ MASTER_ID ของคุณในรายละเอียด) และใช้ STATUS_CONTROL = 'C' เพื่อให้แน่ใจว่าคุณได้รับเวอร์ชันปัจจุบัน อีกทางหนึ่งคุณอาจอ้างอิง ID รายละเอียดเพื่อเชื่อมโยงบางสิ่งกับบางช่วงเวลา
Chris Cameron-Mills

+1 ฉันใช้รูปแบบนี้โดยประสบความสำเร็จอย่างมากในโครงการขนาดใหญ่หลายแห่ง
สามค่าลอจิก

เราใช้ aproach เดียวกัน แต่ตอนนี้ฉันสงสัยว่ามันจะดีกว่าไหมที่จะเก็บเพียง START_DATETIME และไม่เก็บ END_DATETIME
bat_ventzi

ความหลากหลายในประสบการณ์ของฉัน หากเอนทิตีของคุณเป็น "สิ้นสุด" นั่นคือเก็บถาวรหรือลบแล้วคุณจะไม่มีบันทึกรายละเอียดด้วยการควบคุมสถานะ 'C' เช่นไม่มีแถวปัจจุบันแม้ว่าคุณจะไม่รู้ว่าเกิดขึ้นเมื่อใด อีกวิธีหนึ่งคุณสามารถตั้งค่า end_datetime ในแถวสุดท้ายและการมีแถว 'สิ้นสุด' 'C' อาจบ่งบอกว่าตอนนี้เอนทิตีถูกลบ / เก็บถาวร สุดท้ายคุณสามารถแสดงสิ่งนี้ผ่านคอลัมน์อื่นสถานะที่คุณน่าจะมีอยู่แล้ว
Chris Cameron-Mills

15

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

หากคุณลองวิธีอื่นในไม่ช้าคุณจะประสบปัญหา:

  • ค่าใช้จ่ายในการบำรุงรักษา
  • เลือกธงเพิ่มเติม
  • การค้นหาช้าลง
  • การเจริญเติบโตของตารางดัชนี

7

ในSQL Server 2016 และสูงกว่า , มีคุณสมบัติใหม่ที่เรียกว่าตารางชั่วคราวที่มีวัตถุประสงค์เพื่อแก้ปัญหาความท้าทายนี้กับความพยายามน้อยที่สุดจากนักพัฒนา แนวคิดของตารางชั่วคราวคล้ายกับ Change Data Capture (CDC) โดยความแตกต่างที่โต๊ะชั่วคราวได้สรุปสิ่งต่าง ๆ ที่คุณต้องทำด้วยตนเองหากคุณใช้ CDC


2

เปลี่ยนการดักจับข้อมูล: https://docs.microsoft.com/en-us/sql/relational-databases/track-changes/about-change-data-capture-sql-server?view=sql-server-2017

ได้รับการสนับสนุนใน SQL Server 2008 R2 ซึ่งอาจได้รับการสนับสนุนใน SQL Server 2008


โปรดทราบว่าการเปลี่ยนการดักจับข้อมูลนั้นมีไว้สำหรับการจัดเก็บข้อมูลประวัติโดยย่อเท่านั้น ดูตาราง SQL Server ขมับ VS จับเปลี่ยนแปลงข้อมูลเทียบกับการเปลี่ยนแปลงที่ติดตาม
Edward Brey


1

แค่ต้องการเพิ่มตัวเลือกที่ฉันเริ่มใช้เพราะฉันใช้ Azure SQL และสิ่งที่หลายตารางเป็นวิธีที่ยุ่งยากเกินไปสำหรับฉัน ฉันเพิ่มทริกเกอร์การแทรก / อัพเดต / ลบบนโต๊ะของฉันแล้วแปลงก่อน / หลังการเปลี่ยนเป็น json โดยใช้คุณสมบัติ "สำหรับ JSON อัตโนมัติ"

 SET @beforeJson = (SELECT * FROM DELETED FOR JSON AUTO)
SET @afterJson = (SELECT * FROM INSERTED FOR JSON AUTO)

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

ฉันได้เรียนรู้เกี่ยวกับสิ่งนี้จากลิงค์นี้ที่นี่


0

คุณสามารถแบ่งตารางได้หรือไม่

"กลยุทธ์ตารางและดัชนีที่แบ่งพาร์ติชันโดยใช้ SQL Server 2008 เมื่อตารางฐานข้อมูลขยายขนาดเป็นหลายร้อยกิกะไบต์หรือมากกว่านั้นอาจเป็นเรื่องยากที่จะโหลดข้อมูลใหม่ลบข้อมูลเก่าและรักษาดัชนีเพียงขนาดที่แท้จริงของตาราง ทำให้การดำเนินการดังกล่าวใช้เวลานานกว่านั้นแม้ข้อมูลที่ต้องโหลดหรือลบออกอาจมีขนาดใหญ่มากทำให้การดำเนินการ INSERT และ DELETE บนตารางไม่สามารถใช้งานได้ซอฟต์แวร์ฐานข้อมูล Microsoft SQL Server 2008 ให้การแบ่งพาร์ติชันตาราง


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

0

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


2
เป็นเรื่องยากกว่าที่JOINสองตารางในรายงานคู่ที่ผ่านมาหรือมันยากที่จะแก้ไขทุก ๆ การแทรก / อัพเดต / ลบทุก ๆ ตารางเพื่อให้ตระหนักถึงความกังวลในอดีต? ที่จริงแล้วบันทึกการตรวจสอบจะรวมถึงข้อมูลปัจจุบันในตารางประวัติดังนั้นไม่ควรต้องใช้ตารางปัจจุบันในรายงาน

0

อีกทางเลือกหนึ่งคือการเก็บถาวรข้อมูลการดำเนินงานบนพื้นฐาน [รายวัน | รายชั่วโมง | สิ่งใดก็ตาม] เครื่องมือฐานข้อมูลส่วนใหญ่สนับสนุนการสกัดของข้อมูลเป็นที่เก็บ

โดยพื้นฐานแล้วแนวคิดคือการสร้างงาน Windows หรือ CRON ตามกำหนดเวลาที่

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

เอ็นจินฐานข้อมูล SQL จำนวนมากมาพร้อมกับเครื่องมือที่สามารถใช้เพื่อจุดประสงค์นี้ ตัวอย่างเช่นเมื่อใช้ MySQL บน Linux คำสั่งต่อไปนี้สามารถใช้ในงาน CRON เพื่อกำหนดตารางการแตกไฟล์:

mysqldump --all-databases --xml --lock-tables=false -ppassword | gzip -c | cat > /media/bak/servername-$(date +%Y-%m-%d)-mysql.xml.gz

2
สิ่งนี้ไม่เหมาะสำหรับข้อมูลประวัติเพราะถ้าใครบางคนเปลี่ยนค่าและเปลี่ยนกลับภายในวัฏจักรการเก็บถาวรการอัพเดตนั้นจะหายไป นอกจากนี้ยังไม่มีวิธีง่ายๆในการดูการเปลี่ยนแปลงของเอนทิตีหนึ่งเมื่อเวลาผ่านไปหรือเรียกคืนเอนทิตีบางส่วน
Sgoettschkes

0

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

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

ที่เก็บถาวร / ประวัติ : สำหรับกรณีต่างๆเช่นการติดตามที่อยู่ก่อนหน้าหมายเลขโทรศัพท์ ฯลฯ การสร้างตารางแยกต่างหาก FOO_HIST จะดีกว่าถ้าคุณสคีมาของตารางธุรกรรมที่ใช้งานไม่เปลี่ยนแปลงอย่างมีนัยสำคัญในอนาคต (หากตารางประวัติของคุณต้องมีโครงสร้างเดียวกัน) หากคุณคาดว่าจะมีการทำให้เป็นมาตรฐานของตารางประเภทข้อมูลจะเปลี่ยนการเพิ่ม / ลบคอลัมน์ให้เก็บข้อมูลประวัติของคุณในรูปแบบ xml กำหนดตารางที่มีคอลัมน์ต่อไปนี้ (ID, Date, Schema Version, XMLData) สิ่งนี้จะจัดการกับการเปลี่ยนแปลงสคีมาได้อย่างง่ายดาย แต่คุณต้องจัดการกับ xml และนั่นอาจทำให้เกิดความยุ่งยากในการดึงข้อมูล



0

คุณสามารถสร้างมุมมอง materialized / indexed บนตาราง ตามความต้องการของคุณคุณสามารถปรับปรุงมุมมองทั้งหมดหรือบางส่วนได้ โปรดดูสิ่งนี้เพื่อสร้าง mview และบันทึก วิธีการสร้างมุมมอง materialized ใน SQL Server?

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