Merge на PostgreSQL (9.1)
Тестировал на PostgreSQL версии 9.1. На версии 8.4 запрос отказывается работать.
Перед нами стоит задача обновить значения в таблицах измерений. Для примера выберем dim_test.
Первое, что приходит на ум - это использование функции merge. Однако для PostgreSQL функция не реализована.
Решение задачи - использование конструкции CTE для обновления таблиц измерений (обновление через PDI рассмотрим отдельно).
Какие допущения будем предполагать:
- Во входящей таблице измерений из OLTP систем есть уникальный индекс (назовем таблицу dim_test_source).
-
Нас не интересует как таблица (dim_test_source) попадает в нашу БД (можно использовать PDI, для перекидывания).
Вобщем схема такая:
Подготовка таблиц
Зададим таблицу dim_test:
CREATE SEQUENCE dim_test_seq; CREATE TABLE dim_test ( id_dim_test bigint NOT NULL DEFAULT nextval('dim_test_seq'::regclass), id bigint, name text, last_name text, CONSTRAINT id_dim_test_pk PRIMARY KEY (id_dim_test) ); ALTER SEQUENCE dim_test_seq OWNED BY dim_test.id_dim_test ; CREATE INDEX idx_dim_test_tk ON dim_test USING btree (id_dim_test);
id_dim_test - это суррогатный ключ таблицы dim_test или в как встречается в литературе технический ключ.
id - это уникальный ключ dim_test_source.
Создаем входную таблицу dim_test_source:
CREATE SEQUENCE dim_test_source_seq; CREATE TABLE dim_test_source ( id bigint NOT NULL DEFAULT nextval('dim_test_source_seq'), name text, last_name text, CONSTRAINT id_dim_source_pk PRIMARY KEY (id) ) ; ALTER SEQUENCE dim_test_source_seq OWNED BY dim_test_source.id ;
Заполняем ее данными:
INSERT INTO dim_test_source("name", last_name) VALUES ( 'first','test_1'); INSERT INTO dim_test_source("name", last_name) VALUES ( 'second','test_1'); INSERT INTO dim_test_source("name", last_name) VALUES ( 'third','test_1');
Запрос на обновление
Итак запрос обновления будет следующим:
WITH update_dim as ( UPDATE dim_test set "name"=dim_test_source."name", last_name=dim_test_source.last_name from dim_test_source where dim_test.id=dim_test_source.id RETURNING dim_test_source.id ) insert into dim_test ( id, "name", last_name) select dim_test_source.id, dim_test_source."name", dim_test_source.last_name from dim_test_source left join update_dim USING(id) where update_dim.id isnull;
update_dim - таблица состоящая из ключей, у которых обновили запись:
- WITH - конструкция, которая позволяет выполнять повторяющиеся запросы. Мы будем выполнять запрос UPDATE dim_test set "name"=dim_test_source."name" ...для каждого id таблицы dim_test_source.
- Создает таблицу обновленных ключей конструкция RETURNING dim_test_source.id
- Вставляем ключи, у которых нет еще записей. Надо объединить две таблицы. Первая таблица - входная dim_test_source, а вторая update_dim - собранная при помощи RETURNING dim_test_source.id. Используем для объединения left join ( dim_test_source left join update_dim USING(id)) и условие проверки на пустые ключи WHERE update_dim.id isnull
- Конструкция USING(id) эквивалентна ON dim_test_source.id = update_dim.id
Далее, обновим пару записей в таблице dim_test_source:
UPDATE dim_test_source SET name='third_V2' WHERE id = 3; UPDATE dim_test_source SET last_name='test_2' WHERE id = 2;
И добавим данных:
INSERT INTO dim_test_source("name", last_name) VALUES ( 'fourth','test_1'); INSERT INTO dim_test_source("name", last_name) VALUES ( 'fifth','test_1');
Запускаем запрос на обновление еще раз и получаем следующий результат.
Обновление прошло успешно.
Ссылки на материалы:
- PostgreSQL 9.1: эмуляция MERGE с помощью Writable CTE http://dfndr.name/content/postgresql-91-%D1%8D%D0%BC%D1%83%D0%BB%D1%8F%D1%86%D0%B8%D1%8F-merge-%D1%81-%D0%BF%D0%BE%D0%BC%D0%BE%D1%89%D1%8C%D1%8E-writable-cte
- UPSERT/MERGE using Writable CTE in PostgreSQL 9.1 http://vibhorkumar.wordpress.com/2011/10/26/upsertmerge-using-writable-cte-in-postgresql-9-1/
- Получение id добавленной записи в PostgeSQL http://habrahabr.ru/post/72590/
- Batch Update http://tapoueh.org/blog/2013/03/15-batch-update.html
- Подробности
- Опубликовано: 16 Ноябрь 2013
- Просмотров: 3991