데이터베이스 설계 : "아카이브"문제를 처리하는 방법?


18

많은 응용 프로그램, 중요한 응용 프로그램, 은행 등이 매일이 작업을 수행한다고 확신합니다.

그 모든 아이디어는 다음과 같습니다.

  • 모든 행에는 히스토리가 있어야합니다
  • 모든 링크는 일관성을 유지해야합니다
  • "현재"열을 가져 오도록 요청하는 것이 쉬워야합니다.
  • 더 이상 사용하지 않는 제품을 구매 한 고객은이 제품이 더 이상 카탈로그의 일부가 아니더라도 구매 한 제품을 확인해야합니다.

등등.

여기에 내가하고 싶은 일이 있으며, 내가 직면 한 문제에 대해 설명하겠습니다.

모든 테이블에는 해당 열이 있습니다.

  • id
  • id_origin
  • date of creation
  • start date of validity
  • start end of validity

CRUD 작업에 대한 아이디어는 다음과 같습니다.

  • 작성 = id_origin= id, date of creation= now, start date of validity= now,end date of validity = null을 사용 (= 현재 활성 레코드임을 의미)
  • 업데이트 =
    • 읽기 = 모든 레코드 읽기 end date of validity null 인
    • "현재"레코드 end date of validity= null 업데이트end date of validity = now로
    • 새로운 값으로 새로운 end date of validity것을 만들고 = null (= 현재 활성 레코드임을 의미)
  • delete = "현재"레코드를 업데이트합니다 end date of validity= null end date of validity= now

그래서 여기에 내 문제가 있습니다 : 다 대다 협회. 값을 가진 예제를 보자 :

  • 표 A (id = 1, id_origin = 1, start = now, end = null)
  • 표 A_B (시작 = 현재, 종료 = 널, id_A = 1, id_B = 48)
  • 표 B (id = 48, id_origin = 48, start = now, end = null)

이제 테이블 A, 레코드 id = 1을 업데이트하고 싶습니다.

  • end = now로 레코드 id = 1을 표시합니다.
  • 테이블 A에 새 값을 삽입하고 관계를 복제 하지 않으면 관계 A_B를 잃어 버렸 습니다. 이것은 테이블로 끝납니다.

  • 표 A (id = 1, id_origin = 1, start = now, end = now + 8mn)

  • 표 A (id = 2, id_origin = 1, start = now + 8mn, end = null)
  • 표 A_B (시작 = 현재, 종료 = 널, id_A = 1, id_B = 48)
  • 표 A_B (시작 = 현재, 종료 = 널, id_A = 2, id_B = 48)
  • 표 B (id = 48, id_origin = 48, start = now, end = null)

그리고 ... 또 다른 문제가 있습니다 : 관계 A_B : (id_A = 1, id_B = 48)을 더 이상 사용하지 않는 것으로 표시해야합니까 (A-id = 1은 사용되지 않지만 B-48은 아닙니다)?

이것을 다루는 방법?

제품, 파트너 등 대규모로 설계해야합니다.

이것에 대한 당신의 경험은 무엇입니까? 어떻게 하시겠습니까 (어떻게 했습니까)?

-- 편집하다

이 매우 흥미로운 기사를 찾았 지만 "캐스 케이 딩 노후화"(= 실제로 묻는 것)를 제대로 다루지 않습니다.


업데이트 기록의 데이터를 id_hist_prev 필드와 함께 히스토리의 링크 된 목록을 유지하면서 새 ID가있는 새 레코드로 업데이트하기 전에 복사하는 방법은 무엇입니까? 따라서 현재 레코드의 ID는 절대 변경되지 않습니다

휠을 다시 발명하는 대신 Oracle 에서 Flashback Data Archive 를 사용하는 것을 고려 했습니까?
잭 더글러스

답변:


4

이러한 요구 사항이 감사 목적이거나 CRM 및 쇼핑 카트와 같은 간단한 과거 참조인지 여부는 명확하지 않습니다.

어느 쪽이든, 이것이 필요한 각 주요 영역에 대해 main 및 main_archive 테이블을 고려하십시오. "Main"에는 현재 / 활성 항목 만있는 반면 "main_archive"에는 main에 들어간 모든 내용의 사본이 있습니다. main_archive에 삽입 / 업데이트는 main에 삽입 / 업데이트에서 트리거 할 수 있습니다. 그런 다음 main_archive에 대한 삭제는 더 오랜 기간 동안 실행될 수 있습니다.

Cust X와 같은 참조 문제가 제품 Y를 구입 한 경우 cust_archive-> product_archive의 참조 문제를 해결하는 가장 쉬운 방법은 product_archive에서 항목을 삭제하지 않는 것입니다. 일반적으로 해당 테이블에서 이탈이 훨씬 낮아야하므로 크기가 너무 나쁘지 않아야합니다.

HTH.


2
훌륭한 답변이지만 아카이브 테이블을 사용하면 얻을 수있는 또 다른 이점은 비정규 화되어 이러한 데이터에 대한보고가 훨씬 효율적이라는 것입니다. 이 접근 방식으로 응용 프로그램의보고 요구도 고려하십시오.
maple_shaft

1
대부분의 데이터베이스에서 모든 '기본'테이블은 제품 이름의 접두사를 갖습니다. LP_모든 중요한 테이블에는 LH_삽입, 업데이트, 삭제시 히스토리 행을 삽입하는 트리거가 있습니다. 모든 경우에 작동하지는 않지만 내가하는 일에 대한 견고한 모델이었습니다.

동의합니다. 대부분의 쿼리가 "현재"행에 대한 것이라면 기록에서 현재를 두 테이블로 분할하여 성능을 향상시킬 수 있습니다. 편의상 그것들을 다시 하나로 합칠 수 있습니다. 이렇게하면 현재 행이있는 데이터 페이지가 모두 함께 모여 캐시 상태가 좋아질 수 있으며 날짜 논리를 사용하여 현재 데이터에 대한 쿼리를 지속적으로 한정 할 필요가 없습니다.
onupdatecascade

1
@onupdatecascade : (적어도 일부 RDBMS에서는) 해당 UNION뷰 에 인덱스를 넣을 수 있으므로 현재 레코드와 기록 레코드에 고유 제한 조건을 적용하는 것과 같은 멋진 작업을 수행 할 수 있습니다.
모든 거래의 존

5 년 후, 나는 수많은 일을 해왔고 항상 당신의 생각을 되찾았습니다. 내가 변경 한 유일한 것은 기록 테이블에 " id"및 " id_ref" 열이 있다는 것 입니다. id_ref테이블의 실제 아이디어에 대한 참조입니다. 예 : personperson_h. 에 person_h" id"와 관련이 id_ref있는 " " 및 " " id_refperson.id있으므로 동일한 행을 갖는 많은 행을 가질 수 있고 person.id(= 행 person이 수정 될 때 ) id모든 테이블의 모든 테이블이 자동 포함됩니다.
Olivier Pons

2

이것은 함수형 프로그래밍과 약간 중복됩니다. 불변성의 개념.

하나의 테이블이 PRODUCT있고 다른 하나의 테이블이 있습니다 PRODUCTVERSION. 제품을 변경할 때 업데이트를 수행하지 않고 새 PRODUCTVERSION행을 삽입하기 만하면 됩니다. 최신 버전을 얻으려면 버전 번호 (desc), 타임 스탬프 (desc)별로 테이블을 인덱싱하거나 플래그 ( LatestVersion)를 가질 수 있습니다 .

이제 제품을 참조하는 것이 있으면 어떤 테이블을 가리키는 지 결정할 수 있습니다. 그것은 가리 않는 PRODUCT엔티티 (항상이 제품을 말한다) 또는에 PRODUCTVERSION엔티티 (단지 제품의 버전을 의미)?

복잡해집니다. 제품 사진이 있으면 어떻게합니까? 변경 될 수 있기 때문에 버전 테이블을 가리켜 야하지만, 대부분의 경우 변경되지 않으며 데이터를 불필요하게 복제하고 싶지 않습니다. 즉, PICTURE테이블과 다 PRODUCTVERSIONPICTURE대다 관계가 필요합니다.


1

나는 모든 테이블에있는 4 개의 필드 로 여기 에서 모든 것을 구현했습니다 .

  • 신분증
  • date_creation
  • date_validity_start
  • date_validity_end

기록은이 때마다, 나는 그것을 중복 표기의 수정이 중복 "이전"으로 기록 = date_validity_end=NOW()및 좋은 하나와 현재를 date_validity_start=NOW()하고 date_validity_end=NULL.

트릭은 다 대 다와 일 대 다 관계에 관한 것입니다 : 그것들을 건드리지 않고 작동합니다! 더 복잡한 쿼리에 관한 모든 것입니다. 정확한 날짜 (= 지금은 아님) 로 레코드를 쿼리하려면 각 조인과 기본 테이블에 대해 이러한 제약 조건을 추가해야합니다.

WHERE (
  (date_validity_start<=:dateparam AND date_validity_end IS NULL)
  OR
  (date_validity_start<=:dateparam AND date_validity_start>=:dateparam)
)

따라서 제품과 속성 (많은 관계에서)으로 :

SELECT p.*,a.*

FROM products p

JOIN products_attributes pa
ON pa.id_product = p.id
AND (
  (pa.date_validity_start<=:dateparam AND pa.date_validity_end IS NULL)
  OR
  (pa.date_validity_start<=:dateparam AND pa.date_validity_start>=:dateparam)
)

JOIN attributes a
ON a.id = pa.id_attribute
AND (
  (a.date_validity_start<=:dateparam AND a.date_validity_end IS NULL)
  OR
  (a.date_validity_start<=:dateparam AND a.date_validity_start>=:dateparam)
)

WHERE (
  (p.date_validity_start<=:dateparam AND p.date_validity_end IS NULL)
  OR
  (p.date_validity_start<=:dateparam AND p.date_validity_start>=:dateparam)
)

0

이건 어때? 내가 과거에 한 일에 대해 간단하고 효과적입니다. "역사"테이블에서 다른 PK를 사용하십시오. 따라서 "CustomerID"필드는 Customer 테이블의 PK이지만 "history"테이블에서 PK는 "NewCustomerID"입니다. "CustomerID"는 또 다른 읽기 전용 필드가됩니다. 기록에서 "CustomerID"는 변경되지 않고 모든 관계는 그대로 유지됩니다.


아주 좋은 생각입니다. 내가 한 일은 매우 비슷합니다. 레코드를 복제하고 새 레코드를 "사용되지 않음"으로 표시하여 현재 레코드가 여전히 동일합니다. 참고 각 테이블에 트리거를 만들고 싶었지만 mysql 은이 테이블의 트리거에있을 때 테이블 수정을 금지합니다. PostGRESQL이이를 수행합니다. SQL 서버가이를 수행합니다. 오라클은 이것을합니다. 간단히 말해 MySQL은 아직 갈 길이 멀고 다음에 데이터베이스 서버를 선택할 때 두 번 생각할 것입니다.
Olivier Pons
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.