postgresql - каскадное копирование / вставка - PullRequest
2 голосов
/ 23 июля 2011

У меня есть вопрос о копировании строк в PostgreSQL.Моя иерархия таблиц довольно сложная, где многие таблицы связаны друг с другом через внешние ключи.Для простоты я объясню свой вопрос двумя таблицами, но имейте в виду, что мой фактический случай требует гораздо большей сложности.

Скажем, у меня есть следующие две таблицы:

table A
(
    integer identifier primary key
    ... -- other fields
);

table B
(
    integer identifier primary key
    integer a          foreign key references A (identifier)
    ... -- other fields
);

Скажем, A и B содержат следующие строки:

A(1)

B(1, 1)
B(2, 1)

Мой вопрос таков: я хотел бы создать копию строки в A так, чтобы связанные строки в B также копировались в новуюстрока.Это дало бы:

A(1)    -- the old row
A(2)    -- the new row

B(1, 1) -- the old row
B(2, 1) -- the old row
B(3, 2) -- the new row
B(4, 2) -- the new row

В основном я ищу COPY / INSERT CASCADE .

Есть ли хитрый трюк для достижения этого более или менее автоматически?Может быть, используя временные таблицы?

Я считаю, что если мне придется написать все запросы INSERT INTO ... FROM ... в правильном порядке и тому подобном, я могу сойти с ума.

обновление

Давайте ответим на мой собственный вопрос;)

Я провел некоторые попытки с механизмами RULE в PostgreSQL, и это то, что япридумали:

Сначала определения таблиц:

drop table if exists A cascade;
drop table if exists B cascade;

create table A
(
        identifier              serial                  not null                primary key,
        name                    varchar                 not null
);

create table B
(
        identifier              serial                  not null                primary key,
        name                    varchar                 not null,
        a                       integer                 not null                references A (identifier)
);

Далее для каждой таблицы мы создаем функцию и соответствующее правило, которое переводит UPDATE в INSERT.

create function A(in A, in A) returns integer as
$$
        declare
                r integer;
        begin
                -- A
                if ($1.identifier <> $2.identifier) then
                        insert into A (identifier, name) values ($2.identifier, $2.name) returning identifier into r;
                else
                        insert into A (name) values ($2.name) returning identifier into r;
                end if;

                -- B
                update B set a = r where a = $1.identifier;

                return r;
        end;
$$ language plpgsql;

create rule A as on update to A do instead select A(old, new);

create function B(in B, in B) returns integer as
$$
        declare
                r integer;
        begin
                if ($1.identifier <> $2.identifier) then
                        insert into B (identifier, name, a) values ($2.identifier, $2.name, $2.a) returning identifier into r;
                else
                        insert into B (name, a) values ($2.name, $2.a) returning identifier into r;
                end if;

                return r;
        end;
$$ language plpgsql;

create rule B as on update to B do instead select B(old, new);

Наконец, некоторые тесты:

insert into A (name)    values ('test_1');
insert into B (name, a) values ('test_1_child', (select identifier from a where name = 'test_1'));

update A set name = 'test_2', identifier = identifier + 50;
update A set name = 'test_3';

select * from A, B where B.a = A.identifier;

Кажется, это работает вполне нормально.Есть комментарии?

1 Ответ

0 голосов
/ 21 марта 2013

Это будет работать.Одна вещь, которую я замечаю, что вы мудро избегали, это ДЕЛАТЬ ТАКЖЕ правила для вставок и обновлений.ДЕЛАТЬ ТАКЖЕ со вставкой и обновлением довольно опасно, поэтому избегайте этого практически любой ценой.

Однако при дальнейшем рассмотрении триггеры не будут работать хуже и предлагают меньше жестких углов.

...