На мой взгляд, нет смысла использовать NULL для представления логического значения. Семантика is_active предполагает, что единственными разумными значениями являются True и False. Кроме того, NULL мешает каскадным обновлениям.
Поэтому я не использую NULL.
Сначала создайте «родительскую» таблицу с первичным ключом и уникальным ограничением для первичного ключа и «is_active».
create table parent (
p_id integer primary key,
other_columns char(1) default 'x',
is_active boolean not null default true,
unique (p_id, is_deleted)
);
insert into parent (p_id) values
(1), (2), (3);
Создайте дочернюю таблицу со столбцом «is_active». Объявите ограничение внешнего ключа, ссылающееся на столбцы в уникальном ограничении родительской таблицы (последняя строка в операторе CREATE TABLE выше), и выполняйте каскадные обновления.
create table child (
p_id integer not null,
is_active boolean not null default true,
foreign key (p_id, is_active) references parent (p_id, is_active)
on update cascade,
some_other_key_col char(1) not null default '!',
primary key (p_id, some_other_key_col)
);
insert into child (p_id, some_other_key_col) values
(1, 'a'), (1, 'b'), (2, 'a'), (2, 'c'), (2, 'd'), (3, '!');
Теперь вы можете установить для «родителя» значение false, и это будет каскадно распространяться на все ссылочные таблицы.
update parent
set is_active = false
where p_id = 1;
select *
from child
order by p_id;
p_id is_active some_other_key_col
--
1 f a
1 f b
2 t a
2 t c
2 t d
3 t !
Обратимые удаления намного проще и имеют гораздо лучшую семантику, если вы реализуете их в виде таблиц состояний действительного времени. Между прочим, я думаю, что термины обратимое удаление, восстановить и отменить в этом контексте вводят в заблуждение, и я думаю, что вам следует избегать их.
Типы данных диапазона PostgreSQL особенно полезны для такого рода работы. Я использую диапазоны дат, но диапазоны временных меток работают так же.
В этом примере я рассматриваю только «родительский» как таблицу состояний действительного времени. Это означает, что аннулирование определенной строки (обратимое удаление определенной строки) также делает недействительными все строки, которые ссылаются на нее через внешние ключи. Неважно, ссылаются ли они на него прямо или косвенно.
Я не реализую мягкое удаление для «ребенка». Я могу это сделать, но я думаю, что это сделало бы основную технику неоправданно трудной для понимания.
create extension btree_gist; -- Necessary for the kind of exclusion
-- constraint below.
create table parent (
p_id integer not null,
other_columns char(1) not null default 'x',
valid_from_to daterange not null,
primary key (p_id, valid_from_to),
-- No overlapping date ranges for a given value of p_id.
exclude using gist (p_id with =, valid_from_to with &&)
);
create table child (
p_id integer not null,
valid_from_to daterange not null,
foreign key (p_id, valid_from_to) references parent on update cascade,
other_key_columns char(1) not null default 'x',
primary key (p_id, valid_from_to, other_key_columns),
other_columns char(1) not null default 'x'
);
Вставьте некоторые образцы данных. В PostgreSQL тип данных daterange имеет специальное значение «бесконечность». В данном контексте это означает, что строка со значением 1 для «parent». «p_id» действительна с «2015-01-01» до бесконечности.
insert into parent values
(1, 'x', daterange('2015-01-01', 'infinity'));
insert into child values
(1, daterange('2015-01-01', 'infinity'), 'a', 'x'),
(1, daterange('2015-01-01', 'infinity'), 'b', 'y');
Этот запрос покажет вам соединенные строки.
select *
from parent p
left join child c
on p.p_id = c.p_id
and p.valid_from_to = c.valid_from_to;
Чтобы сделать строку недействительной, обновите диапазон дат. Эта строка (ниже) была действительна с 01.01.2015 по 31.01.2015. То есть он был мягко удален 31 января 2015 года.
update parent
set valid_from_to = daterange('2015-01-01', '2015-01-31')
where p_id = 1 and valid_from_to = daterange('2015-01-01', 'infinity');
Вставьте новую допустимую строку для p_id 1 и выберите дочерние строки, которые были признаны недействительными 31 января.
insert into parent values (1, 'r', daterange(current_date, 'infinity'));
update child set valid_from_to = daterange(current_date, 'infinity')
where p_id = 1 and valid_from_to = daterange('2015-01-01', '2015-01-31');
Основополагающая книга Ричарда Т. Снодграсса Разработка приложений баз данных, ориентированных на время, на SQL доступна бесплатно по адресу веб-страница его университета.
person
Mike Sherrill 'Cat Recall'
schedule
12.04.2015