이 질문에 올바르게 대답하려면 먼저 다음을 결정해야합니다. 이 시스템 / 응용 프로그램에서 "삭제"는 무엇을 의미합니까?
이 질문에 대답하려면 또 다른 질문에 대답해야합니다. 왜 레코드가 삭제됩니까?
사용자가 데이터를 삭제해야하는 여러 가지 이유가 있습니다. 일반적으로 삭제가 필요한 이유 는 정확히 하나의 이유 (테이블 당)입니다. 몇 가지 예는 다음과 같습니다.
- 디스크 공간을 회수합니다.
- 보존 / 개인 정보 보호 정책에 따라 강제 삭제가 필요합니다.
- 부정확하거나 부정확하게 잘못된 데이터는 복구하는 것보다 삭제 및 재생이 더 쉽습니다.
- 대부분 의 행은, 예를 들어, 로그 테이블 X 레코드 제한 / 일 삭제됩니다.
하드 삭제에 대한 몇 가지 매우 나쁜 이유 도 있습니다 (나중에 이러한 이유에 대한 자세한 내용 참조).
- 사소한 오류를 수정합니다. 이것은 보통 개발자 게으름과 적대적인 UI를 강조합니다.
- 거래 (예 : 청구 된 적이없는 송장)를 "공제"합니다.
- 당신이 할 수 있기 때문에 .
왜 그렇게 큰 문제입니까? 좋은 올레의 문제는 무엇입니까 DELETE
?
- 돈에 먼 거리에있는 시스템에서도 하드 삭제는 아카이브 / 비석 테이블로 이동하더라도 모든 종류의 회계 기대치를 위반합니다. 이를 처리하는 올바른 방법은 소급 이벤트 입니다.
- 아카이브 테이블은 라이브 스키마와 다른 경향이 있습니다. 새로 추가 된 열이나 계단식을 잊어 버린 경우 해당 데이터를 영구적으로 잃어버린 것입니다.
- 하드 삭제는 특히 계단식 작업에서 매우 비싼 작업 일 수 있습니다 . 많은 사람들이 인식하지 않는 하나 개 이상의 수준을 계단식 (또는 경우에 따라 어떤 계단식, DBMS에 따라) 레코드 수준의 작업을 대신 설정 작업에서 발생합니다.
- 반복적이고 빈번한 하드 삭제는 인덱스 조각화 프로세스 속도를 높입니다.
따라서 소프트 삭제가 더 낫습니다. 아니 정말:
- 캐스케이드 설정은 매우 어려워집니다. 거의 항상 클라이언트에 고아 행으로 표시됩니다.
- 하나의 삭제 만 추적 할 수 있습니다. 행이 여러 번 삭제 및 삭제 취소 된 경우 어떻게합니까?
- 분할, 뷰 및 / 또는 필터링 된 인덱스를 사용하면 다소 완화 될 수 있지만 읽기 성능이 저하됩니다.
- 앞에서 언급했듯이 일부 시나리오 / 관할 구역에서는 실제로 불법 일 수 있습니다.
진실은이 두 가지 접근 방식이 모두 틀렸다는 것입니다. 삭제가 잘못되었습니다. 실제로이 질문을하는 경우 거래 대신 현재 상태를 모델링하고 있음 을 의미 합니다. 이것은 데이터베이스 랜드에서 나쁜 습관입니다.
Udi Dahan은 이에 대해 Do n't Delete-Just Do n't 에서 썼습니다 . 이 항상 어떤 종류의 작업, 거래, 활동 , 또는 (내 추천 용어) 이벤트 실제로 "삭제"를 나타냅니다. 나중에 성능을 위해 "현재 상태"테이블로 비정규 화하려는 경우에도 괜찮지 만 이전이 아닌 트랜잭션 모델을 정리 한 후에 수행 하십시오.
이 경우 "사용자"가 있습니다. 사용자는 본질적으로 고객입니다. 고객은 귀하와 비즈니스 관계를 맺고 있습니다. 그 관계는 계정을 취소했기 때문에 단순히 허풍으로 사라지지 않습니다. 실제로 일어나고있는 일은 :
- 고객이 계정을 만듭니다
- 고객이 계정을 취소 함
- 고객이 계정을 갱신
- 고객이 계정을 취소 함
- ...
모든 경우에 동일한 고객 이며 가능하면 동일한 계정입니다 (즉, 각 계정 갱신은 새로운 서비스 계약입니다). 그렇다면 왜 행을 삭제합니까? 이것은 모델링하기가 매우 쉽습니다.
+-----------+ +-------------+ +-----------------+
| Account | --->* | Agreement | --->* | AgreementStatus |
+-----------+ +-------------+ +----------------+
| Id | | Id | | AgreementId |
| Name | | AccountId | | EffectiveDate |
| Email | | ... | | StatusCode |
+-----------+ +-------------+ +-----------------+
그게 다야. 그것이 전부입니다. 아무것도 삭제할 필요가 없습니다. 위의 방법은 유연성이 뛰어나지 만 약간 단순화 할 수있는 상당히 일반적인 디자인입니다. "계약"레벨이 필요하지 않고 "계정"이 "계정 상태"테이블로 이동하도록 결정할 수 있습니다.
응용 프로그램에서 자주 필요의 목록을 얻을 경우 활성 계약 / 그것 (약간) 까다로운 쿼리의 다음 계정,하지만의는 무엇을보기위한 것입니다 :
CREATE VIEW ActiveAgreements AS
SELECT agg.Id, agg.AccountId, acc.Name, acc.Email, s.EffectiveDate, ...
FROM AgreementStatus s
INNER JOIN Agreement agg
ON agg.Id = s.AgreementId
INNER JOIN Account acc
ON acc.Id = agg.AccountId
WHERE s.StatusCode = 'ACTIVE'
AND NOT EXISTS
(
SELECT 1
FROM AgreementStatus so
WHERE so.AgreementId = s.AgreementId
AND so.EffectiveDate > s.EffectiveDate
)
그리고 당신은 끝났습니다. 이제 소프트 삭제의 모든 이점이 있지만 단점은 없습니다.
- 모든 레코드가 항상 표시되므로 고아 레코드는 문제가 아닙니다. 필요할 때마다 다른보기에서 선택하면됩니다.
- "삭제"는 일반적으로 매우 저렴한 작업입니다. 이벤트 테이블에 한 행만 삽입하면됩니다.
- 어떤 역사를 잃을 기회가 결코 지금까지 , 아니 당신이 나사 방식 심하게 문제는.
- 당신은 여전히 계정을 하드 삭제할 수 있다면 당신은 (개인 정보 보호를 위해 예) 필요, 및 삭제가 깨끗하게되지 일어날 응용 프로그램 / 데이터베이스의 다른 부분을 방해하는 지식 편안하게.
해결해야 할 유일한 문제는 성능 문제입니다. 많은 경우 실제로 클러스터 된 인덱스로 인해 실제로 문제가 아닌 것으로 밝혀졌습니다. AgreementStatus (AgreementId, EffectiveDate)
I / O 탐색은 거의 없습니다. 그러나 문제가 발생하면 트리거, 인덱스 / 구체화 된 뷰, 응용 프로그램 수준 이벤트 등을 사용하여이를 해결할 수있는 방법이 있습니다.
너무 일찍 성능에 대해 걱정하지 마십시오. 디자인을 올바르게하는 것이 더 중요합니다.이 경우 "올바른"은 데이터베이스를 트랜잭션 시스템 으로 사용하는 방식으로 데이터베이스를 사용한다는 의미 입니다.