2012-05-01 3 views
2

У меня очень простая таблица students, структура, как показано ниже, где первичный ключ id. Эта таблица представляет собой стенд для около 20 многомиллионных таблиц строк, которые объединяются вместе.Поддержание логической согласованности с мягким удалением при сохранении исходной информации

 
+----+----------+------------+ 
| id | name | dob  | 
+----+----------+------------+ 
| 1 | Alice | 01/12/1989 | 
| 2 | Bob  | 04/06/1990 | 
| 3 | Cuthbert | 23/01/1988 | 
+----+----------+------------+ 

Если Боб хочет изменить свою дату рождения, то у меня есть несколько вариантов:

  1. Update students с новой датой рождения.

    Положительные: 1 операция DML; к таблице всегда можно получить доступ с помощью одного первичного ключа.

    Отрицательный: я теряю тот факт, что Боб когда-либо думал, что он родился 04/06/1990

  2. Добавить колонку, created date default sysdate, к столу и изменить первичный ключ id, created. Каждый update становится:

    insert into students(id, name, dob) values (:id, :name, :new_dob) 
    

    Затем, когда я хочу, самая последняя информация не выполните следующие действия (Oracle, но вопрос стоит для каждого RDBMS):

    select id, name, dob 
        from (select a.*, rank() over (partition by id 
                 order by created desc) as "rank" 
          from students a) 
    where "rank" = 1 
    

    Положительных: Я никогда не теряют какой-либо информации ,

    Отрицательные: Все запросы по всей базе данных занимают немного больше времени. Если таблица указана в указанном размере, это не имеет значения, но как только вы на своем 5-м left outer join, используя сканирование по диапазону, а не уникальное сканирование начинает иметь эффект.

  3. Добавить другой столк, deleted date default to_date('2100/01/01','yyyy/mm/dd'), или еще что-то слишком раннее, или футуристическое, дата принимает мое воображение. Измените первичный ключ id, deleted затем каждый update становится:

    update students x 
        set deleted = sysdate 
    where id = :id 
        and deleted = (select max(deleted) from students where id = x.id); 
    insert into students(id, name, dob) values (:id, :name, :new_dob); 
    

    и запрос, чтобы получить из текущей информации становится:

    select id, name, dob 
        from (select a.*, rank() over (partition by id 
                 order by deleted desc) as "rank" 
          from students a) 
    where "rank" = 1 
    

    Положительных: Я никогда не теряют какой-либо информации.

    Отрицательные: Две операции DML; Я все еще должен использовать ранжированные запросы с дополнительной стоимостью или сканирование диапазона, а не уникальное сканирование индекса в каждом запросе.

  4. Создайте вторую таблицу, скажет student_archive и изменить каждое обновление в:

    insert into student_archive select * from students where id = :id; 
    update students set dob = :newdob where id = :id; 
    

    Положительные: Никогда не теряйте никакой информации.

    Отрицательные: 2 DML-операции; если вы когда-либо захотите получить всю информацию, когда-либо вы должны использовать union или дополнительно left outer join.

  5. Для полноты есть чудовищно де-нормированная структура данных: id, name1, dob, name2, dob2... и т.д.

Если номер 1 не вариант, если я не хочу потерять какую-либо информацию и всегда делать мягкие удаления. Номер 5 можно безопасно отбросить, поскольку это вызывает больше проблем, чем того стоит.

У меня остались варианты 2, 3 и 4 с их негативными аспектами. Обычно я использую опцию 2 и потрясающие 150 строк (хорошо распределенные) множественные суб-выборки, которые идут вместе с ним.


Т.Л., д-р Я понимаю, что катание близко к линии на «не конструктивные» голосованиях здесь, но:

Что такое оптимального (в единственном числе!) Способ поддержания логического непротиворечивость, а never удаление любых данных?

Есть ли более эффективный способ, чем те, которые я документировал? В этом контексте я буду определять эффективность как «меньше операций DML» и/или «возможность удалить подзапросы». Если вы можете думать о лучшем определении, когда (если) отвечаете, пожалуйста, не стесняйтесь.

ответ

1

Я бы придерживался # 4 с некоторыми изменениями. Не нужно удалять данные из исходной таблицы; достаточно скопировать старые значения в архивную таблицу перед обновлением (или перед удалением) оригинальной записи. Это можно легко сделать с помощью триггера уровня. Извлечение всей информации, на мой взгляд, не является частым действием, и я не вижу ничего плохого в дополнительном соединении/объединении. Кроме того, вы можете определить представление, так что все запросы будут простыми с точки зрения конечного пользователя.

+0

Спасибо! Я изменил # 4 на 'insert ... update'; очевидно, не слишком много думал об этом. – Ben