[250222] Developing Time-Oriented Applications in SQL
Table of Contents
1 Chapter 7. Modification
이 장에서는 유효 시간 테이블에 대한 수정을 구현을 설명한다. 현재(현재부터 영구적으로 영향을 미침), 시퀀스(지정된 기간에 영향을 미침), 비시퀀스(타임스탬프를 명시적으로 조작)의 세 가지 유형의 수정을 고려합니다.
주요 내용:
- 현재 수정 사항:
- 현재 삽입의 경우 시작 날짜를 현재로, 종료 날짜를 영구적으로 설정.
- 현재의 삭제는 종료일을 현재로 설정하는 UPDATE로 구현되고, 이후에 미래 행에 대한 DELETE가 실행.
- 현재의 업데이트에는 세 가지 명령문이 필요: 새로운 값을 삽입하는 INSERT, 종료일을 업데이트하는 UPDATE, 미래 행을 업데이트하는 UPDATE
- 시퀀스 수정:
- 시퀀스 삽입은 적용 기간을 지정.
- 시퀀스 삭제는 네 가지 명령문이 필요: 이전 값을 유지하는 INSERT, 날짜를 조정하는 두 개의 UPDATE, 그리고 DELETE
- 시퀀스 업데이트는 다양한 기간 중복 사례를 처리하기 위해 5개의 문장이 필요.
- 다른 템포럴 테이블을 참조할 때는 더 복잡해짐.
- 비시퀀스 수정:
- 실제로는 드물다.
- SQL로 표현하기는 쉽지만 영어로 표현하기는 어렵다.
- 각각의 경우에서 무결성 제약 조건에 미치는 영향을 신중하게 고려해야 한다.
- 템포럴 파티셔닝:
- 유효 시간 테이블을 현재 부분과 과거 부분으로 나눈다.
- 일부 작업은 단순화하지만, 다른 작업은 복잡하게 만든다.
- 쿼리의 단순성과 수정의 복잡성 사이의 균형
이 장에서는 현재의 수정 방식이 가장 일반적(비템포럴 테이블의 매칭 동작)이지만, 시퀀스 수정은 기간을 신중하게 처리해야 하고 훨씬 더 복잡한 SQL 문을 필요로 한다는 점을 강조한다. 적절한 접근 방식은 응용 프로그램의 특정 요구 사항에 따라 달라진다.
핵심적인 통찰력은 현재의 수정 사항이 레거시 비템포럴 애플리케이션에서 사용하는 것이기 때문에 템포럴 지원을 추가할 때 구현하기가 가장 쉽다는 것이다. 보다 복잡한 템포럴 수정을 위해서는 데이터 무결성을 유지하면서 기간을 적절하게 유지하기 위해 보다 정교한 SQL 코드가 필요하다.
1.1 Current modifications
1.1.1 Current insertions
-- CF 7.1 -- Bob joins as associate director of the Computer Science. INSERT INTO INCUMBENTS VALUES (111223333, 999071, CURRENT_DATE, '3000-01-01');
이 SQL은 “지금(now)”부터 시간의 끝(3000-01-01)까지의 타임스탬프를 이용하여 Bob의 현재상태를 추가한다.
테이블의 현재 상태만 수정된다면 시퀀스화된 기본 키를 유지하는 것은 쉽다.
-- CF 7.2 -- Bob joins as associate director of the Computer Center, ensuring the -- primary key, in the restricted case. INSERT INTO INCUMBENTS (SSN, PCN, START_DATE, END_DATE) SELECT DISTINCT 111223333, 999071, CURRENT_DATE, DATE '3000-01-01' FROM DUAL WHERE NOT EXISTS (SELECT * FROM INCUMBENTS AS I2 WHERE SSN = I2.SSN AND PCN = I2.PCN AND I2.END_DATE = DATE '3000-01-01');
이것이 가능한 이유는 역시 END_DATE
가 '3000-01-01' 로 영원을 의미하도록 설계되었기 때문이다.
그리고 현재형태로만 테이블을 다룬다면 허용이 되는 것이다. 만약 (미래 ~ 영원) 을 다루는 데이터가 있다면 이걸로는 부족하다.
1.1.2 Current Deletions
테이블에서 현재(current) 수정만 허용되는 제한된 경우, 간단하게 업데이트로 변환됩니다.
-- CF 7.3 -- Bob resigns from the Computer Center. (only current modification assumed) UPDATE INCUMBENTS SET END_DATE = CURRENT_DATE WHERE SSN = 111223333 AND PCN = 999071 AND END_DATE = DATE '3000-01-01';
이 SQL은 현재 날짜를 종료 날짜로 설정하여 Bob의 현재 상태를 삭제한다.
1.1.3 Current updates
-- CF 7.9 Today Bob was promoted to director of the Computer Center. (non-temporal version) UPDATE INCUMBENTS SET PCN = 908739 WHERE SSN = 111223333
-- CF 7.10 -- Today Bob was promoted to director of the Computer Center -- (assuming only current modifications). INSERT INTO INCUMBENTS (SSN, PCN, START_DATE, END_DATE) SELECT DISTINCT SSN, 908739, CURRENT_DATE, DATE '3000-01-01' FROM INCUMBENTS WHERE SSN = 111223333 AND END_DATE = DATE '3000-01-01' UPDATE INCUMBENTS SET END_DATE = CURRENT_DATE WHERE SSN = 111223333 AND START_DATE < CURRENT_DATE
일반적으로는 세단계가 필요하다. 미래에도 데이터가 있다면 그 데이터도 수정이 필요하다.
-- Today Bob was promoted to director of the Computer Center (general case) INSERT INTO INCUMBENTS (SSN, PCN, START_DATE, END_DATE) SELECT DISTINCT SSN, 908739, CURRENT_DATE, DATE '3000-01-01' FROM INCUMBENTS WHERE SSN = 111223333 AND START_DATE <= CURRENT_DATE AND END_DATE > CURRENT_DATE; UPDATE INCUMBENTS SET END_DATE = CURRENT_DATE WHERE SSN = 111223333 AND START_DATE < CURRENT_DATE AND END_DATE > CURRENT_DATE; UPDATE INCUMBENTS SET PCN = 908739 WHERE SSN = 111223333 AND START_DATE > CURRENT_DATE;
1.2 Sequence modifications
1.2.1 Sequence insertions
데이터를 넣을 때 적용기간(valid-time period)을 지정한다.
-- CF 7.12 -- Bob was assigned the position of associate director of the Computer Center for 1997. INSERT INTO INCUMBENTS (SSN, PCN, START_DATE, END_DATE) VALUES (111223333, 999071, DATE '1997-01-01', DATE '1997-12-31');
기간을 표현할 때, 개방-폐쇠 구간(open-closed interval)을 사용한다. 기본키를 보장하기 위해 적용기간동안에 어떤 중복도 허용하지 않는다. 이를 위해서 CF.7-5(서적참고)의 약간의 일반화가 필요.
--CF 7.5 -- Bob was assigned the position of associate director of the Computer Center, -- ensuring the primary key, in the unrestricted case. INSERT INTO INCUMBENTS (SSN, PCN, START_DATE, END_DATE) SELECT DISTINCT SSN, 999071, DATE '1997-01-01', DATE '1998-01-01' FROM DUAL WHERE NOT EXISTS (SELECT * FROM INCUMBENTS AS I2 WHERE SSN = I2.SSN AND PCN = I2.PCN AND I2.END_DATE = DATE '3000-01-01'); -- CF 7.13 -- Bob was assigned the position of associate director of the Computer Center for 1997, -- ensuring the primary key. INSERT INTO INCUMBENTS (SSN, PCN, START_DATE, END_DATE) SELECT DISTINCT SSN, 999071, DATE '1997-01-01', DATE '1998-01-01' FROM DUAL WHERE NOT EXISTS (SELECT * FROM INCUMBENTS AS I2 WHERE SSN = I2.SSN AND PCN = I2.PCN -- 기간 중복 체크 AND I2.START_DATE <= DATE '1998-01-01' AND DATE '1997-01-01' < I2.END_DATE);
참조 무결성을 유지하기 위해 다시 두 가지 선택지가 있을 것이다.
- 제약 조건을 위반할 경우 삽입을 허용하지 않거나
- 삽입하기 전에 공백을 채우는 것.
제약 조건의 위반을 허용하지 않으려면 CF-5.21에서 가져온 강력한 술어(Predicate)가 필요하다.
Sequenced insertion ensuring uniqueness and referential integrity
Insert a row if no duplicate exists during the period of applicability, and if there is a row in the referenced table at the start of the period of applicability, and if there is a row at the end of the period of applicability, and if there are no gaps during the period of applicability.
– 한국어 시퀀스 삽입으로 고유성과 참조 무결성 보장
적용 기간 동안 중복이 존재하지 않고, 적용 기간 시작 시점에 참조된 테이블에 행이 있고, 적용 기간 종료 시점에 행이 있고, 적용 기간 동안 공백이 없는 경우 행을 삽입한다.
이것을 적용한 예제는 다음과 같다.
-- CF 7.14 -- Bob was assigned the position of associate director of the Computer Center for 1997, -- also ensuring referential integrity. Code Fragment 7.14 Bob was assigned the position of associate director of the Computer Center for 1997, also ensuring referential integrity. INSERT INTO INCUMBENTS (SSN, PCN, START_DATE, END_DATE) SELECT DISTINCT 111223333, 999071, DATE '1997-01-01', DATE '1998-01-01' FROM POSITIONS -- 적용기간동안 중복이 없다. WHERE NOT EXISTS ( SELECT * FROM INCUMBENTS AS I2 WHERE SSN = I2.SSN AND PCN = I2.PCN AND I2.START_DATE < DATE '1998-01-01' AND DATE '1997-01-01' < I2.END_DATE) -- 적용기간 시작 시점에 참조된 테이블(POSITIONS)에 행이 있다. AND EXISTS ( SELECT * FROM POSITIONS AS P WHERE P.PCN = 999071 AND P.START_DATE <= DATE '1997-01-01' AND DATE '1997-01-01' < P.END_DATE) -- 적용기간 종료 시점에 참조된 테이블(POSITIONS)에 행이 있다. AND EXISTS ( SELECT * FROM POSITIONS AS P WHERE P.PCN = 999071 AND P.START_DATE < DATE '1998-01-01' AND DATE '1998-01-01' <= P.END_DATE) -- 적용기간 동안 공백이 없다. AND NOT EXISTS ( SELECT * FROM POSITIONS AS P WHERE P.PCN = 999071 AND DATE '1997-01-01' < P.END_DATE AND P.END_DATE < DATE '1998-01-01' AND NOT EXISTS ( SELECT * FROM POSITIONS AS P2 WHERE P2.PCN = 999071 AND P2.START_DATE <= P.END_DATE AND P.END_DATE < P2.END_DATE))
1.2.2 Sequence deletions
Sequenced Deletion은 지정된 기간(period of applicability) 동안의 데이터를 삭제하는 작업이다.
nontemporal deletion(시간에 대한 정보가 없는 삭제)부터 시작하자.
-- CF 7.15 -- Bob was removed as associate director of the Computer Center(nontemporal version). DELETE FROM INCUMBENTS WHERE SSN=111223333 AND PCN = 999071
이제 우리는 특정 기간에서만 삭제하고자 한다. ("Bob was removed as associate director of the Computer Center for 1997")
핵심 개념:
- 기존 row의 유효기간(period of validity)과 삭제하려는 기간(period of applicability)의 상호작용을 고려해야 한다.
- 4가지 주요 케이스가 있으며, 각각 다르게 처리해야 한다.
그림으로 표현하면 다음과 같다.
Case 1: 원본 row가 삭제 기간을 포함 PV: ├──────────────────────┤ (Period of Validity) PA: ├────┤ (Period of Applicability) 결과: ├────┤ ├─────────┤ (두 조각으로 분리) Case 2: 삭제 기간이 끝부분과 겹침 PV: ├────────────┤ PA: ├──────┤ 결과: ├────┤ (앞부분만 유지) Case 3: 삭제 기간이 시작부분과 겹침 PV: ├────────────┤ PA: ├──────┤ 결과: ├─────────┤ (뒷부분만 유지) Case 4: 삭제 기간이 row를 완전히 포함 PV: ├────┤ PA: ├──────────────┤ 결과: (완전히 삭제) (row 전체 삭제)
- Case 1: 원본 row가 삭제 기간을 완전히 포함하는 경우
- 초기와 최종 기간을 모두 유지해야 함
- 초기 기간: end date를 삭제 기간 시작으로 설정
- 최종 기간: 새로운 row로 삽입
- Case 2: 삭제 기간이 row의 끝 부분과 겹치는 경우
- 초기 부분만 유지
- end date를 삭제 기간 시작으로 수정
- Case 3: 삭제 기간이 row의 시작 부분과 겹치는 경우
- 최종 부분만 유지
- start date를 삭제 기간 끝으로 수정
- Case 4: 삭제 기간이 row 전체를 포함하는 경우
- row 전체 삭제
구현:
-- CF 7.16 -- Bob was removed as associate director of the Computer Center for 1997, INSERT INTO INCUMBENTS SELECT SSN, PCN, DATE '1998-01-01', END_DATE FROM INCUMBENTS WHERE SSN = 111223333 AND PCN = 999071 AND START_DATE < DATE '1997-01-01' AND END_DATE > DATE '1997-01-01' UPDATE INCUMBENTS SET END_DATE = DATE '1997-01-01' WHERE SSN = 111223333 AND PCN = 999071 AND START_DATE < DATE '1997-01-01' AND END_DATE >= DATE '1997-01-01' UPDATE INCUMBENTS SET START_DATE = DATE '1998-01-01' WHERE SSN = 111223333 AND PCN = 999071 AND START_DATE < DATE '1998-01-01' AND END_DATE >= DATE '1998-01-01' DELETE FROM INCUMBENTS WHERE SSN = 111223333 AND PCN = 999071 AND START_DATE >= DATE '1997-01-01' AND END_DATE <= DATE '1998-01-01'
주의사항:
- 문장들은 반드시 정해진 순서대로 실행되어야 한다.
- 일시적으로 primary key 제약조건이 위반될 수 있으므로, 제약조건 검사를 연기해야 할 수 있다.
(case 1 의 경우 INSERT 및 UPDATE 를 수행하는데 INSERT 를 하는 순간 제약이 위반될 수 있다.
SQL-92 는 이러한 경우를 위해 제약조건 검사를 연기하는
SET CONSTRAINTS DEFERRED
를 제공한다.) - Sequenced deletion은 nonsequenced uniqueness 제약조건을 위반할 수 있다.
이러한 복잡성에도 불구하고, sequenced deletion은 temporal 데이터의 정확한 이력 관리를 위해 필수적인 기능이다.
1.2.3 Sequence updates
nontemporal version UPDATE 는 다음과 같다.
-- CF 7.17 -- Bob was promoted to director of the Computer Center. UPDATE INCUMBENTS SET PCN = 908739 WHERE SSN = 111223333
이것을 이제 sequenced version으로 변환해보자.
4가지 기본 케이스:
Case 1: 원본 row가 적용 기간을 포함 PV: ├──────────────────────┤ (Period of Validity) PA: ├──────┤ (Period of Applicability) 결과: ├────┤ ├───┤ ├─────────┤ (3개 조각) (원값) (신값) (원값) Case 2: 적용 기간이 끝부분과 겹침 PV: ├────────────┤ PA: ├──────┤ 결과: ├────┤ ├───┤ (원값 + 신값) Case 3: 적용 기간이 시작부분과 겹침 PV: ├────────────┤ PA: ├──────┤ 결과: ├───┤ ├────┤ (신값 + 원값) Case 4: 적용 기간이 row를 완전히 포함 PV: ├────┤ PA: ├──────────────┤ 결과: ├───┤ (전체 업데이트)
구현에는 5개의 SQL 문장이 필요합니다:
-- 1. 적용 기간 이전의 원래 값 보존 INSERT INTO INCUMBENTS SELECT SSN, PCN, START_DATE, DATE '1997-01-01' FROM INCUMBENTS WHERE SSN = 111223333 AND START_DATE < DATE '1997-01-01' AND END_DATE > DATE '1997-01-01' -- 2. 적용 기간 이후의 원래 값 보존 INSERT INTO INCUMBENTS SELECT SSN, PCN, DATE '1998-01-01', END_DATE FROM INCUMBENTS WHERE SSN = 111223333 AND START_DATE < DATE '1998-01-01' AND END_DATE > DATE '1998-01-01' -- 3. 적용 기간 동안의 값 수정 UPDATE INCUMBENTS SET PCN = 908739 WHERE SSN = 111223333 AND START_DATE < DATE '1998-01-01' AND END_DATE > DATE '1997-01-01' -- 4. Start date 수정 UPDATE INCUMBENTS SET START_DATE = DATE '1997-01-01' WHERE SSN = 111223333 AND START_DATE < DATE '1997-01-01' AND END_DATE > DATE '1997-01-01' -- 5. End date 수정 UPDATE INCUMBENTS SET END_DATE = DATE '1998-01-01' WHERE SSN = 111223333 AND START_DATE < DATE '1998-01-01' AND END_DATE > DATE '1998-01-01'
주요 특징:
- 모든 문장은 정해진 순서대로 실행되어야 함
- 일시적인 Primary Key 제약조건 위반 가능
- 각 케이스마다 적절한 처리 필요
- UPDATE는 삭제와 삽입의 조합으로 볼 수 있음
다른 temporal 테이블을 참조하는 경우 세 가지 기간을 고려해야 함:
- 수정되는 row의 유효기간
- 수정의 적용기간
- 참조되는 테이블의 row 유효기간
1.3 modifications that mention other tables
다른 테이블을 참조하는 수정 만약 predecate(술어) 또는 SET 절이 subquery를 포함하거나 다른 테이블을 언급한다면 어떻게 해야 하는가.
1.3.1 Complex Current Modification
Current 쿼리에는 STARTDATE <= CURRENTDATE AND CURRENTDATE < ENDDATE 조건만 추가하면 됨
예시: Bob을 Computer Center의 director로 승진시키는 케이스
-- CF 7.21 Non-temporal version UPDATE INCUMBENTS SET PCN = (SELECT PCN FROM POSITIONS, JOB_TITLES WHERE POSITIONS.JOB_TITLE_CODE1 = JOB_TITLE_CODE AND JOB_TITLE = 'DIRECTOR, COMPUTER CENTER') WHERE SSN = '111223333' -- 7.22 Current version (temporal) INSERT INTO INCUMBENTS SELECT SSN, I.PCN, CURRENT_DATE, I.END_DATE FROM INCUMBENTS AS I, POSITIONS, JOB_TITLES WHERE SSN = '111223333' AND I.START_DATE <= CURRENT_DATE AND I.END_DATE > CURRENT_DATE AND POSITIONS.JOB_TITLE_CODE1 = JOB_TITLE_CODE AND JOB_TITLE = 'DIRECTOR, COMPUTER CENTER' AND POSITIONS.START_DATE <= CURRENT_DATE AND CURRENT_DATE < POSITIONS.END_DATE -- 2. 기존 row의 END_DATE 업데이트 UDPATE INCUMBENTS SET END_DATE = CURRENT_DATE WHERE SSN = '111223333' AND PCN <> (SELECT PCN FROM POSITIONS, JOB_TITLES WHERE POSITIONS.JOB_TITLE_CODE1 = JOB_TITLE_CODE AND JOB_TITLE = 'DIRECTOR, COMPUTER CENTER' AND POSITIONS.START_DATE <= CURRENT_DATE AND CURRENT_DATE < POSITIONS.END_DATE) AND INCUMBENTS.START_DATE < CURRENT_DATE AND INCUMBENTS.END_DATE > CURRENT_DATE -- 3. 미래의 rows 업데이트 UPDATE INCUMBENTS SET PCN = (SELECT PCN FROM POSITIONS, JOB_TITLES WHERE POSITIONS.JOB_TITLE_CODE1 = JOB_TITLE_CODE AND JOB_TITLE = 'DIRECTOR, COMPUTER CENTER' AND POSITIONS.START_DATE <= CURRENT_DATE AND CURRENT_DATE < POSITIONS.END_DATE) WHERE SSN = '111223333' AND START_DATE >= CURRENT_DATE
1.3.2 Complex Sequenced Modifications (7.4.2)
아주복잡한 SQL 필요.
세 가지 기간을 고려해야 함:
- 수정되는 row의 유효기간 (Period of Validity for modified row)
- 수정의 적용기간 (Period of Applicability)
- 참조되는 테이블의 row 유효기간 (Period of Validity for referenced row)
6 라인의 non-temporal update가 76 라인의 sequenced 버전으로 확장되는 등 매우 복잡해짐.
nontemporal update 예시
-- CF 7.23 Bob was promoted to director of the Computer Center. UPDATE INCUMBENTS SET PCN = (SELECT PCN FROM POSITIONS, JOB_TITLES WHERE POSITIONS.JOB_TITLE_CODE1 = JOB_TITLE_CODE AND JOB_TITLE = 'DIRECTOR, COMPUTER CENTER') WHERE SSN = '111223333' -- CF 7.24 -- Bob was promoted to director of the Computer Center for 1997. (sequenced version) INSERT INTO INCUMBENTS SELECT SSN, PCN, DATE '1998-01-01', END_DATE FROM INCUMBENTS WHERE SSN = '111223333' AND START_DATE < DATE '1998-01-01' AND END_DATE > DATE '1998-01-01' INSERT INTO INCUMBENTS SELECT SSN, PCN, START_DATE, DATE '1997-01-01' FROM INCUMBENTS WHERE SSN = '111223333' AND START_DATE < DATE '1997-01-01' AND END_DATE > DATE '1997-01-01' UPDATE INCUMBENTS SET PCN = (SELECT PCN FROM POSITIONS, JOB_TITLES WHERE POSITIONS.JOB_TITLE_CODE1 = JOB_TITLE_CODE AND JOB_TITLE = 'DIRECTOR, COMPUTER CENTER' AND POSITIONS.START_DATE <= DATE '1997-01-01' AND DATE '1997-01-01' < POSITIONS.END_DATE) WHERE SSN = '111223333' AND START_DATE < DATE '1998-01-01' AND END_DATE > DATE '1998-01-01' INSERT INTO INCUMBENTS SELECT SSN, PCN, POSITION.START_DATE, POSITION.END_DATE FROM INCUMBENTS, POSITIONS, JOB_TITLES WHERE SSN = '111223333' AND INCUMBENTS.START_DATE <= DATE '1998-01-01' AND INCUMBENTS.END_DATE > DATE '1998-01-01' AND POSITIONS.JOB_TITLE_CODE1 = JOB_TITLE_CODE AND JOB_TITLE = 'DIRECTOR, COMPUTER CENTER' AND DATE '1997-01-01' < POSITIONS.START_DATE AND INCUMBENTS.START_DATE < POSITIONS.START_DATE AND POSITIONS.END_DATE < DATE '1998-01-01' AND POSITIONS.END_DATE < INCUMBENTS.END_DATE INSERT INTO INCUMBENTS SELECT SSN, PCN, POSITION.START_DATE, DATE '1998-01-01' FROM INCUMBENTS, POSITIONS, JOB_TITLES WHERE SSN = '111223333' AND INCUMBENTS.START_DATE <= DATE '1998-01-01' AND INCUMBENTS.END_DATE > DATE '1998-01-01' AND POSITIONS.JOB_TITLE_CODE1 = JOB_TITLE_CODE AND JOB_TITLE = 'DIRECTOR, COMPUTER CENTER' AND DATE '1997-01-01' < POSITIONS.START_DATE AND DATE '1998-01-01' < POSITIONS.END_DATE UPDATE INCUMBENTS SET END_DATE = DATE '1998-01-01' WHERE SSN = '111223333' AND START_DATE < DATE '1998-01-01' AND END_DATE > DATE '1998-01-01' AND NOT EXISTS (SELECT * FROM INCUMBENTS AS I2 WHERE INCUMBENTS.SSN = I2.SSN AND INCUMBENTS.PCN = I2.PCN AND INCUMBENTS.START_DATE < I2.END_DATE AND I2.START_DATE < INCUMBENTS.END_DATE) UPDATE INCUMBENTS SET END_DATE = (SELECT MIN(I2.START_DATE) FROM INCUMBENTS AS I2 WHERE INCUMBENTS.SSN = I2.SSN AND INCUMBENTS.PCN <> I2.PCN AND INCUMBENTS.START_DATE < I2.START_DATE) WHERE SSN = '111223333' AND START_DATE < DATE '1998-01-01' AND END_DATE > DATE '1998-01-01' AND EXISTS (SELECT * FROM INCUMBENTS AS I2 WHERE INCUMBENTS.SSN = I2.SSN AND INCUMBENTS.PCN <> I2.PCN AND INCUMBENTS.START_DATE < I2.START_DATE AND I2.START_DATE < INCUMBENTS.END_DATE)
1.4 7.5 Temporal partitioning
7.5장 "Temporal Partitioning"은 유효 시간 상태 테이블에서 CURRENTDATE를 열 값으로 저장할 수 없는 SQL 및 DBMS의 한계를 피하기 위한 전략으로, 테이블을 현재 정보만 포함하는 "현재 저장소"(current store)와 과거 정보를 포함하는 "과거 저장소"(history store)로 분할하는 방법을 설명함. 이 접근법은 공간 효율성을 높이고, "지금(now)"을 나타내는 ENDDATE 값을 동적으로 관리할 필요를 제거함.
문서에 따르면, =CURRENTDATE=는 매일 변경되므로 열 값으로 저장할 수 없으며, 이는 DBMS 벤더들에게 불편한 점이지만 사용자 입장에서는 Bob의 직책과 같은 데이터가 매일 변경될 수 있는 현실을 반영해야 한다. 시간적 분할은 이러한 문제를 해결하며, 데이터베이스의 시간적 진화를 효과적으로 관리할 수 있게 한다.
- 시간적 분할의 필요
유효 시간 테이블에서 =ENDDATE=를 '3000-01-01'("forever")로 설정하여 "현재(now)"를 나타내지만, 이는 Y3K 문제를 초래하거나 동적 업데이트가 어렵다.
CURRENT_DATE=를 열 값으로 저장하려면 DBMS가 이를 지원해야 하지만, 현재 SQL 표준에서는 허용되지 않으며,
대신 =CURRENT_DATE - INTERVAL '1' DAY
같은 상대적 값도 지원되지 않음.
- 분할 전략
INCUMBENTS
테이블을 둘로 분할한다.
INCUMBENTS.CURRENT
테이블은 현재 정보만 포함함. END_DATE
열을 생략하며, START_DATE
은 언제 정보가 유효해졌는지 나타낸다.
INCUMBENTS.PAST
테이블은 과거에 유효했던 데이터를 포함하며, =STARTDATE=와 =ENDDATE=로 기간을 명시한다.
- 장점
- 현재 데이터 관리 간소화: INCUMBENTS.CURRENT는 현재 유효한 데이터만 포함하므로 쿼리와 수정이 더 빠르고 간단함.
- "지금(now)" 문제 해결: ENDDATE를 동적으로 관리할 필요 없이 "forever" 값이나 CURRENTDATE 문제를 피함.
- 과거 데이터 보존: INCUMBENTS.PAST는 과거 이력을 유지하며, 시간 슬라이스 쿼리나 시퀀스 쿼리에 유용함.
- 구현예시
문서에서는 직접적인 SQL 코드 조각을 제공하지 않지만, Chapter 9의 9.4.1 "Current and Archival Stores"에서 트랜잭션 시간 테이블의 분할 방식을 참고할 수 있음. 유효 시간 테이블에 적용하면 다음과 유사할 수 있음:
CREATE TABLE INCUMBENTS.PAST ( SSN CHAR(9), JOB_TITLE_CODE INT, START_DATE DATE, END_DATE DATE, PRIMARY KEY (SSN, START_DATE) ); CREATE TABLE INCUMBENTS.CURRENT ( SSN CHAR(9), JOB_TITLE_CODE INT, START_DATE DATE DEFAULT CURRENT_DATE, PRIMARY KEY (SSN) );
INCUMBENTS.CURRENT
의 =STARTDATE=는 기본값으로 =CURRENTDATE=를 사용하며, =ENDDATE=는 생략되어 현재 상태를 암시적으로 나타낸다.
- 사용 시나리오
예를 들어, 대학에서 직원 승진이 늦은 봄에 발표되고 다음 회계 연도(7월 1일)부터 적용된다면, Bob의 새로운 직책은 7월 1일 =STARTDATE=로 INCUMBENTS.CURRENT에 저장된다.
이 데이터는 아직 현재가 아니므로 =INCUMBENTS.PAST=에 적합하지 않으며, 분할 구조가 이러한 상황을 효과적으로 처리.
- 제한사항
- 과거 데이터 쿼리는
INCUMBENTS.PAST=와 =INCUMBENTS.CURRENT=를 결합해야 하므로 =UNION
연산이 필요할 수 있음. - 레거시 코드가
INCUMBENTS
테이블을 직접 참조하도록 설계된 경우, 분할된 테이블로의 전환을 위해 수정이 필요할 수 있음. - 또한 미래데이터를 저장할수 없음. CURRENT 테이블은 현재 데이터이고, PAST 테이블은 과거 데이터이다. 즉, 표현력이 원래 INCUMBENTS 테이블보다 제한적이다. 예를들어,
- 결론
7.5장 "Temporal Partitioning"은 유효 시간 상태 테이블을 현재와 과거로 분할하여 CURRENTDATE의 제약을 피하고, 공간 및 성능 효율성을 높이는 방법을 제시. 이는 Bob의 직책과 같은 동적 데이터의 관리를 단순화하며, 시간 데이터베이스의 유지 관리를 개선. 분할된 테이블은 현재 데이터에 대한 빠른 접근을 제공하고, 과거 이력을 보존하여 복잡한 시간 쿼리를 지원.
1.4.1 Quries
템포럴 파티셔닝을 사용하는 경우, 현재 쿼리는 더 간단. 현재 저장소에 쿼리를 적용하기만 하면 된다.
-- Code Fragment 7.25 What is Bob's current position? SELECT JOB_TITLE_CODE1FROM EMPLOYEES, INCUMBENTS_ CURRENT, POSITIONS WHERE FIRST_NAME = 'Bob' AND EMPLOYEES.SSN= INCUMBENTS_CURRENT.SSN AND INCUMBENTS_CURRENT.PCN = POSITIONS.PCN
시간적 분할의 단점은, 아래에서 보겠듯이, 과거 데이터를 다루는 쿼리가 두 테이블을 참조해야 한다는 점. 그러나 때로는 데이터가 그 데이터의 출처(source of data)에서 자연스럽게 분할되어 있다. (데이터가 적절히 분할되어있어 자연스럽다.) 116페이지를 보면, SALHISTORY 테이블은 급여 이력을 포함하고, EMPLOYEES 테이블은 현재 급여와 PAYCHANGEDATE를 포함한다.. 지금 언급하려는 중요한 설계 문제는 과거 테이블에 현재 정보를 복제할지 여부이다.
이들 접근법은 모두 길이 면에서 유사(약 30줄의 SQL 코드). 그러나 시간적 분할이 기본 테이블 중 하나 또는 둘 다에 적용될 경우, 쿼리가 훨씬 더 복잡해진다. INCUMBENTS 테이블이 INCUMBENTS.PAST와 INCUMBENTS.CURRENT로 분할된 경우, 시간 조인은 50줄 쿼리로 확장된다.
Code Fragment 7.26 Provide the salary and department history for all employees. ( Insert CF-6.11 from page 151, substituting INCUMBENTS_PAST for INCUMBENTS) UNION SELECT S.SSN, AMOUNT, PCN, S.HISTORY_START_DATE, S.HISTORY_END_DATE FROM SAL_HISTORY AS S, INCUMBENTS_CURRENT AS I WHERE S.SSN = I.SSN AND I.START_DATE <= S.HISTORY_START_DATE UNION SELECT S.SSN, AMOUNT, PCN, I.START_DATE, S.HISTORY_END_DATE FROM SAL_HISTORY AS S, INCUMBENTS_CURRENT AS I WHERE S.SSN = I.SSN AND I.START_DATE > S.HISTORY_START_DATE AND S.HISTORY_END_DATE <= DATE '3000-01-01' AND I.START_DATE < S.HISTORY_END_DATE UNION SELECT S.SSN, AMOUNT, PCN, I.START_DATE, S.HISTORY_END_DATE FROM SAL_HISTORY AS S, INCUMBENTS_CURRENT AS I WHERE S.SSN = I.SSN AND I.START_DATE > S.HISTORY_START_DATE AND DATE '3000-01-01' < S.HISTORY_END_DATE
대충 이런식으로 UNION 을 이용해 과거 테이블과 현재 테이블을 합체해야 한다.
1.4.2 Modifications
현재 데이터 삽입은 역시나 쉽다
Code Fragment 7.27 Bob was assigned the position of associate director of the Computer Center (partitioned). INSERT INTO INCUMBENTS_CURRENT VALUES (111223333, 6201945234, CURRENT_DATE)
다음은 중복이 없을 시에만 삽입이 가능하도록 하는 예시
코드 조각 7.28 Bob was assigned the position of associate director of the Computer Center (partitioned, avoiding sequenced duplicates). INSERT INTO INCUMBENTS_CURRENT VALUES (111223333, 6201945234, CURRENT_DATE) WHERE NOT EXISTS ( SELECT * FROM INCUMBENTS_CURRENT AS I2 WHERE I2.SSN = 111223333 AND I2.PCN = 6201945234)
다음은 현재 테이블에서 삭제하는 예시.
현재 삭제(current deletion)는 현재 저장소(current store)에서 기록 저장소(history store)로 행을 이동.
-- Code Fragment 7.29 Bob was fired as associate director of the Computer Center (partitioned). -- 현재 레코드를 INCUMBENTS_PAST 테이블로 이동시키는 INSERT 문 INSERT INTO INCUMBENTS_PAST (SSN, PCN, START_DATE, END_DATE) SELECT SSN, PCN, START_DATE, CURRENT_DATE FROM INCUMBENTS_CURRENT WHERE SSN = 111223333 AND PCN = 999071 -- INCUMBENTS_CURRENT 테이블에서 해당 레코드를 삭제하는 DELETE 문 DELETE FROM INCUMBENTS_CURRENT WHERE SSN = 111223333 AND PCN = 999071
분할된 테이블에 대한 순차적 수정은 두 저장소(현재, 과거 테이블)를 모두 고려해야 한다.
- 순차적 수정에 대한 설명:
- 분할된 테이블은 보통 현재 데이터(INCUMBENTSCURRENT)와 과거 데이터(INCUMBENTSPAST)를 별도로 저장.
- 순차적 삽입은 두 가지 경우가 있다:
- 적용 기간이 완전히 과거에 있으면 → 이력 저장소에 삽입
- 적용 기간이 현재를 포함하면 → 현재 저장소에 삽입
- 순차적 삭제에 대한 설명:
- 분할된 저장소는 미래 데이터를 직접 기록할 수 없다.
- 삭제 시 적용 기간은 현재 이전에 끝나거나 "영원히" 계속되어야 한다.
- 첫 번째 경우: 이력 저장소만 수정
- 두 번째 경우: 이력 저장소와 현재 저장소 모두 수정
Code Fragment 7.30 Bob was removed as associate director of the Computer Center for 1997 (partitioned). INSERT INTO INCUMBENTS_PAST SELECT SSN, PCN, DATE '1998-01-01', END_DATE FROM INCUMBENTS_PAST WHERE SSN = 111223333 AND PCN = 999071 AND START_DATE <= DATE '1997-01-01' AND END_DATE > DATE '1998-01-01' UPDATE INCUMBENTS_PAST SET END_DATE = DATE '1997-01-01' WHERE SSN = 111223333 AND PCN = 999071 AND START_DATE < DATE '1997-01-01' AND END_DATE >= DATE '1997-01-01' UPDATE INCUMBENTS_PAST SET START_DATE = DATE '1998-01-01' WHERE SSN = 111223333 AND PCN = 999071 AND START_DATE < DATE '1998-01-01' AND END_DATE >= DATE '1998-01-01' DELETE FROM INCUMBENTS_PAST WHERE SSN = 111223333 AND PCN = 999071 AND START_DATE >= DATE '1997-01-01' AND END_DATE <= DATE '1998-01-01' UPDATE INCUMBENTS_CURRENT SET START_DATE = DATE '1998-01-01' WHERE SSN = 111223333 AND PCN = 999071 AND START_DATE <= DATE '1998-01-01' INSERT INTO INCUMBENTS_PAST SELECT SSN, PCN, START_DATE, DATE '1997-01-01' FROM INCUMBENTS_CURRENT WHERE SSN = 111223333 AND PCN = 999071 AND START_DATE < DATE '1997-01-01' UPDATE INCUMBENTS_CURRENT SET START_DATE = DATE '1998-01-01' WHERE SSN = 111223333 AND PCN = 999071 AND START_DATE < DATE '1998-01-01' DELETE FROM INCUMBENTS_CURRENT WHERE SSN = 111223333 AND PCN = 999071 AND START_DATE >= DATE '1997-01-01'
아래는 Bob이 1997년에 컴퓨터 센터의 디렉터로 승진했음을 나타내는 파티셔닝된 데이터베이스 테이블(INCUMBENTSPAST 및 INCUMBENTSCURRENT)에 대한 SQL 업데이트 쿼리를 보여준다.
INSERT INTO INCUMBENTS_PAST SELECT SSN, PCN, START_DATE, DATE '1997-01-01' FROM INCUMBENTS_PAST WHERE SSN = 111222333 AND START_DATE < DATE '1997-01-01' AND END_DATE > DATE '1997-01-01' INSERT INTO INCUMBENTS_PAST SELECT SSN, PCN, DATE '1998-01-01', END_DATE FROM INCUMBENTS_PAST WHERE SSN = 111222333 AND START_DATE < DATE '1998-01-01' AND END_DATE > DATE '1998-01-01' UPDATE INCUMBENTS_PAST SET PCN = 908739 WHERE SSN = 111222333 AND START_DATE < DATE '1998-01-01' AND END_DATE > DATE '1997-01-01' UPDATE INCUMBENTS_PAST SET START_DATE = DATE '1997-01-01' WHERE SSN = 111222333 AND START_DATE < DATE '1997-01-01' AND END_DATE > DATE '1997-01-01' UPDATE INCUMBENTS_PAST SET END_DATE = DATE '1998-01-01' WHERE SSN = 111222333 AND START_DATE < DATE '1998-01-01' AND END_DATE > DATE '1998-01-01' INSERT INTO INCUMBENTS_PAST SELECT SSN, PCN, START_DATE, DATE '1997-01-01' FROM INCUMBENTS_CURRENT WHERE SSN = 111222333 AND START_DATE < DATE '1997-01-01' INSERT INTO INCUMBENTS_PAST SELECT SSN, 908739, DATE '1997-01-01', DATE '1998-01-01' FROM INCUMBENTS_CURRENT WHERE SSN = 111222333 AND START_DATE < DATE '1997-01-01' AND DATE '1998-01-01' < CURRENT_DATE INSERT INTO INCUMBENTS_PAST SELECT SSN, 908739, START_DATE, DATE '1998-01-01' FROM INCUMBENTS_CURRENT WHERE SSN = 111222333 AND START_DATE < DATE '1998-01-01' AND DATE '1998-01-01' < CURRENT_DATE UPDATE INCUMBENTS_PAST SET PCN = 908739 WHERE SSN = 111222333 AND START_DATE < DATE '1998-01-01' AND END_DATE > DATE '1997-01-01' UPDATE INCUMBENTS_CURRENT SET START_DATE = DATE '1998-01-01' WHERE SSN = 111222333 AND START_DATE < DATE '1998-01-01' UPDATE INCUMBENTS_CURRENT SET START_DATE = DATE '1997-01-01' WHERE SSN = 111222333 AND START_DATE < DATE '1997-01-01'
이렇게 테이블이 CURRENT, PAST 두 개로 나뉘어 있으면, 쿼리가 더 길어진다.