powered by simpleCommunicator - 2.0.61     © 2026 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / PostgreSQL [игнор отключен] [закрыт для гостей] / Пакетное добавление/обновление данных
25 сообщений из 32, страница 1 из 2
Пакетное добавление/обновление данных
    #38674561
cr@nk
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Доброго времени суток.
Подскажите пожалуйста, как в PostgreSQL реализованы пакетные операции?
Немного подробностей:
Клиентское приложение написано на Delphi (если это важно)
Приложение каждый день парсит файлы Excel и заносит инфу в БД. На каждую запись из файла excel приходятся следующие проверки:
Считали строку (несколько ячеек) в переменные
Формируем запрос на поиск совпадения АРтикулИзФайла и АртикулИзБазы
Если совпадений нет, то добавляем запись
Если найдено одно совпадение, то происходит проверка НаименованиеИзФайла и НаиманованиеИзБазы. Если есть различия, то вызывается UPDATE

Мне посоветовали пакетные операции в PostgreSQL? но я не совсем понял как это реализуется в СУБД
Я так понимаю, что на стороне клиента надо формировать либо запрос в памяти либо какой-то файл специального формата в каждой строчке которого будет вызываться
INSERT (строка 1)
INSERT (строка 2)
А на стороне PostgreSQL как-то реализовать хранимую процедуру или триггер (так и не понял, что лучше), которая будет осуществлять все описанные выше операции
...
Рейтинг: 0 / 0
Пакетное добавление/обновление данных
    #38674593
V&N
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
V&N
Гость
cr@nk, заливайте Excel во временную таблицу, а потом insert/update из временной таблицы.
все равно перечитываете весь файл и дергаете базу.
...
Рейтинг: 0 / 0
Пакетное добавление/обновление данных
    #38674628
cr@nk
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
V&N,
Пара вопросов:
Есть какое-то специальное понятие временной таблицы? Или имеется ввиду создавать таблицу, заливать туда данные, переносить их в новую таблицу, а ту временную удалять?

Допустим, что есть 2 таблицы (основная и временная). Я не понимаю как их объединить с обновлением данных
Условно структура такая: ID (serial), Article, Name, Date, Cost, Post (integer, подстановочное поле, берётся из другой таблицы). Остальные поля все типа text и именно они берутся из файла Excel
В этой таблице поле Article не изменяется. Может поменяться Name, Cost и 100% поменяется Date на следующий день. Как PG произведёт слияние 2х таблиц?
...
Рейтинг: 0 / 0
Пакетное добавление/обновление данных
    #38674632
cr@nk
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Если это важно, то объём данных из файлов Excel самый разный. Может быть пара тысяч строк, а может доходить до 100 тыс строк
...
Рейтинг: 0 / 0
Пакетное добавление/обновление данных
    #38674645
V&N
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
V&N
Гость
cr@nk ,
временная - CREATE TEMPORARY TABLE ... IF NOT EXISTS ON COMMIT { PRESERVE ROWS | DELETE ROWS | DROP ...
- доступна в пределах сессии.

UPDATE table
FROM temp_table
WHERE ....

INSERT INTO table (col1, col2, col...) (SELECT col1, col2, col... FROM temp_table WHERE NOT EXISTS /NOT IN ..)
...
Рейтинг: 0 / 0
Пакетное добавление/обновление данных
    #38674651
V&N
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
V&N
Гость
cr@nkЕсли это важно, то объём данных из файлов Excel самый разный. Может быть пара тысяч строк, а может доходить до 100 тыс строк
ну и что, а так вы пинаете базу на каждую сроку.

а так три раза пнуть 1) залить лимон строк, 2) обновление расхождений 3) заливка нового.
...
Рейтинг: 0 / 0
Пакетное добавление/обновление данных
    #38674671
cr@nk
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
V&N,

Спасибо за разъяснение. Получается никакие триггеры даже не нужны :)

Ещё есть вопрос-уточнение про временные таблицы:
1. Надо ли внутри временной таблицы прописывать создание индексов?
2. По поводу жизни таблицы на время сессии. У меня может быть запущено несколько копий моей программы для обработки разных файлов Excel. Все они коннектятся с одним логином и паролем к БД. После обработки прога закрывается. Вот я и думаю, что будет, если прописать в программе жёстко одно и тоже имя для временной таблицы? Или лучше генерировать имя таблицы из случайного набора символов?
...
Рейтинг: 0 / 0
Пакетное добавление/обновление данных
    #38674683
LeXa NalBat
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
заливайте много строк сразу через команду COPY

а вот эту логику сделайте в тригере FOR EACH ROW:
cr@nkЕсли совпадений нет, то добавляем запись
Если найдено одно совпадение, то происходит проверка НаименованиеИзФайла и НаиманованиеИзБазы. Если есть различия, то вызывается UPDATE
...
Рейтинг: 0 / 0
Пакетное добавление/обновление данных
    #38674700
V&N
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
V&N
Гость
cr@nkV&;N,

Спасибо за разъяснение. Получается никакие триггеры даже не нужны :)

Ещё есть вопрос-уточнение про временные таблицы:
1. Надо ли внутри временной таблицы прописывать создание индексов?
2. По поводу жизни таблицы на время сессии. У меня может быть запущено несколько копий моей программы для обработки разных файлов Excel. Все они коннектятся с одним логином и паролем к БД. После обработки прога закрывается. Вот я и думаю, что будет, если прописать в программе жёстко одно и тоже имя для временной таблицы? Или лучше генерировать имя таблицы из случайного набора символов?

нужны/не нужны триггеры - вам решать.

1. Возможно понадобится на ключевые поля. explain analyze в помощь
2. Все равно.
...
Рейтинг: 0 / 0
Пакетное добавление/обновление данных
    #38674739
cr@nk
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
LeXa NalBat,

А вашего метода какие преимущества перед методом, предложенным выше?
...
Рейтинг: 0 / 0
Пакетное добавление/обновление данных
    #38674786
cr@nk
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
LeXa NalBatзаливайте много строк сразу через команду COPY
Вы имеете ввиду приводить xls-файл к CSV формату и потом импортировать в таблицу?
...
Рейтинг: 0 / 0
Пакетное добавление/обновление данных
    #38674846
LeXa NalBat
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
cr@nkLeXa NalBat,

А вашего метода какие преимущества перед методом, предложенным выше?мне кажется, упрощается интерфейс обновления данных в таблице. вы делаете из приложения просто INSERT или COPY. а всю работу за вас сделает тригер, вместо INSERT-а выполнит UPSERT.

вот ваша задача упоминается в документации: http://www.postgresql.org/docs/current/static/plpgsql-control-structures.html#PLPGSQL-UPSERT-EXAMPLE

аналогичная задача возникла и у нас, но не обновлять справочник, а дополнять счетчики статистики. я сделал с использованием тригера, сейчас испольуется в бою:
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
create function usage_upsert() returns trigger language plpgsql as
$$
declare
    rc integer;
begin
    execute '
    update
        ' || quote_ident(tg_table_name) || '
    set
        req_begin = req_begin + ' || new.req_begin || '
        , req_started = req_started + ' || new.req_started || '
        , req_max = req_max + ' || new.req_max || '
        , req_dur = req_dur + ' || new.req_dur || '
        , sess_begin = sess_begin + ' || new.sess_begin || '
        , sess_started = sess_started + ' || new.sess_started || '
        , sess_max = sess_max + ' || new.sess_max || '
        , sess_dur = sess_dur + ' || new.sess_dur || '
        , bytes_out = bytes_out + ' || new.bytes_out || '
        , bytes_out_cached = bytes_out_cached + ' || new.bytes_out_cached || '
    where
        time_id = ' || new.time_id || '
        and geo_id = ' || new.geo_id || '
        and useragent_id = ' || new.useragent_id || '
        and backend_id = ' || new.backend_id || '
        and proto_id = ' || new.proto_id || '
        and clienturi_id = ' || new.clienturi_id || '
    ';
    get diagnostics rc = row_count;
    if rc > 0 then
        return null;
    end if;
    return new;
end;
$$;

create trigger usage_2014_06_upsert before insert on usage_2014_06
    for each row execute procedure usage_upsert() ;


cr@nkLeXa NalBatзаливайте много строк сразу через команду COPYВы имеете ввиду приводить xls-файл к CSV формату и потом импортировать в таблицу?не нужно сохранять файл в формате csv. из вашего языка порграммирования вызываете функцию типа pg_copy(table_name, data). например perl, python предоставляют такую функцию. не знаю, умеет ли это драйвер дельфи, вот нагуглилось: тынц
...
Рейтинг: 0 / 0
Пакетное добавление/обновление данных
    #38674855
biwed.ru
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
cr@nk,
Можно реализовать (insert/update) и без триггера через с помощью CTE, если версия PostgreSQL позволяет. Реализовывал подобное http://www.biwed.ru/index.php/dobryaki/16-sql/36-obnovlenie-zapisej-tablicy-izmerenij-pri-pomoshhi-cte

С уважением,
biwed.ru
...
Рейтинг: 0 / 0
Пакетное добавление/обновление данных
    #38674882
cr@nk
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
biwed.rucr@nk,
Можно реализовать (insert/update) и без триггера через с помощью CTE, если версия PostgreSQL позволяет. Реализовывал подобное http://www.biwed.ru/index.php/dobryaki/16-sql/36-obnovlenie-zapisej-tablicy-izmerenij-pri-pomoshhi-cte
Может быть мне не хватает грамотности в этом вопросе, но этот пример является просто развёрнутым ответом участника форума V&N

LeXa NalBatмне кажется, упрощается интерфейс обновления данных в таблице. вы делаете из приложения просто INSERT или COPY. а всю работу за вас сделает тригер, вместо INSERT-а выполнит UPSERT.
Да я тоже склоняюсь, что лучше основную работу возложить на сервер.
Надо просто разобраться с командой COPY
...
Рейтинг: 0 / 0
Пакетное добавление/обновление данных
    #38676465
cr@nk
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Пробую написать триггерную функцию для своей задачи. Пока получается вот что:
Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
CREATE OR REPLACE FUNCTION my_upsert()
  RETURNS trigger AS
$BODY$DECLARE
_tovar_count integer;

BEGIN
	SELECT COUNT(*) INTO _tovar_count FROM _tovar WHERE article=new.article;
	IF _tovar_count=0 THEN
		RETURN NEW;
	ELSIF _tovar_count=1 THEN
		IF 'name'<>new.name THEN
			UPDATE _tovar SET name=new.name WHERE id=id;
		END IF;
		IF 'lastdate'<=new.lastdate THEN
			UPDATE _tovar SET lastdate=new.lastdate WHERE id=id;
		END IF;
	END IF;
	RETURN NULL;
END;$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;
ALTER FUNCTION my_upsert()
  OWNER TO postgres;


Получаю ошибку
pgAdminОШИБКА: неверный синтаксис для типа date: "lastdate"
LINE 1: SELECT 'lastdate'<=new.lastdate
^
QUERY: SELECT 'lastdate'<=new.lastdate
CONTEXT: функция PL/pgSQL my_upsert(), строка 12, оператор IF
********** Ошибка **********
...
Рейтинг: 0 / 0
Пакетное добавление/обновление данных
    #38676469
cr@nk
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Если взять в кавычки и вторую часть выражения, то ошибка пропадает, но само сравнение не отрабатывает корректно. Попробовал менять new.lastdate на +/- 1 день. Запрос ниже всегда выполнялся
Код: plsql
1.
IF 'lastdate'<=new.lastdate THEN
...
Рейтинг: 0 / 0
Пакетное добавление/обновление данных
    #38676475
cr@nk
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Конечно же, условие прописано ошибочное:
Код: plsql
1.
WHERE (article=new.article) AND (distrib=new.distrib);
...
Рейтинг: 0 / 0
Пакетное добавление/обновление данных
    #38676494
cr@nk
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Мда... осознал, что функция в целом не работает
Код: plsql
1.
IF 'name'<>new.name THEN


Подскажите пожалуйста, как это дело реализовать правильно...
(сравнить переданное значение (new) с уже имеющимся)
...
Рейтинг: 0 / 0
Пакетное добавление/обновление данных
    #38676577
LeXa NalBat
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
cr@nk,

вам надо выбрать все поля строки, если такая имеется, из таблицы с интересующим значением artice.
то есть вместо "SELECT COUNT(*) INTO _tovar_count FROM ..." надо делать что-то типа:
SELECT _tovar INTO _found_tovar FROM _tovar WHERE article=new.article;
и далее сравнивать new.name с _found_tovar.name и т.п.

и еще, условие "WHERE id=id" тождественное, его тоже надо заменить. и, возможно, оставить таким же как в SELECT, то есть по artice, а не по id.
...
Рейтинг: 0 / 0
Пакетное добавление/обновление данных
    #38676677
cr@nk
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
LeXa NalBat,

Спасибо за наводку. Вроде сейчас получил рабочую функцию
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
CREATE OR REPLACE FUNCTION my_upsert()
  RETURNS trigger AS
$BODY$DECLARE
	_tovar_found record;
	_tovar_count Integer;
BEGIN
	SELECT * INTO _tovar_found FROM _tovar WHERE (article=new.article) AND (distrib=new.distrib);
	SELECT COUNT(*) INTO _tovar_count FROM _tovar WHERE (article=new.article) AND (distrib=new.distrib);
	IF _tovar_count=0 THEN
		RETURN NEW;
	ELSIF _tovar_count=1 THEN
		IF _tovar_found.name<>new.name THEN
			UPDATE _tovar SET name=new.name WHERE id=_tovar_found.id;
		END IF;
		IF _tovar_found.lastdate<=new.lastdate THEN
			IF _tovar_found.cost<>new.cost THEN
				UPDATE _tovar SET lastdate=new.lastdate, cost=new.cost WHERE id=_tovar_found.id;
			ELSE
				UPDATE _tovar SET lastdate=new.lastdate WHERE id=_tovar_found.id;
			END IF;
		END IF;
	END IF;
	RETURN NULL;
END;$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;
ALTER FUNCTION my_upsert()
  OWNER TO postgres;



Так и не смог понять, как другим способом получить количество полученных записей через
Код: sql
1.
SELECT * INTO _tovar_found FROM _tovar WHERE (article=new.article) AND (distrib=new.distrib);



Ещё бы как-то отладить это дело. Не вижу какие индексы использует функция и использует ли в принципе
...
Рейтинг: 0 / 0
Пакетное добавление/обновление данных
    #38676693
Troglodit
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Я может чего не понял, но что мешает реализовать всю эту функцию одним Update с логикой, чем разводить код типа
Код: sql
1.
SELECT * INTO _tovar_found FROM _tovar WHERE (article=new.article) AND (distrib=new.distrib);


который вернет ВСЕ поля в таблице и делать два UPDATE на позицию + обвязка логики.
...
Рейтинг: 0 / 0
Пакетное добавление/обновление данных
    #38676696
cr@nk
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Troglodit,

Явная нехватка знаний в этой области. Подскажите пож-та как сделать
...
Рейтинг: 0 / 0
Пакетное добавление/обновление данных
    #38677068
Ivan Durak
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
LeXa NalBatмне кажется, упрощается интерфейс обновления данных в таблице. вы делаете из приложения просто INSERT или COPY. а всю работу за вас сделает тригер, вместо INSERT-а выполнит UPSERT.

ну да, проще но сильно тормознее.
p.s. триггеры зло!
...
Рейтинг: 0 / 0
Пакетное добавление/обновление данных
    #38677123
cr@nk
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Ivan Durak,

Я бы не сказал, что сильно проще. Вон, товарищ постом выше говорит, что всё решается как-то через 1 UPDATE...
Это по скорости наверное космос будет в сочетании с правильными индексами.
Надо просто замеры скорости сделать в обоих вариантах, но для начала надо триггерную функцию оптимизировать :)
...
Рейтинг: 0 / 0
Пакетное добавление/обновление данных
    #38677460
LeXa NalBat
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Ivan Durakну да, проще но сильно тормознее.вот что говорит EXPLAIN ANALYZE, через тригер проходит более тысячи строк в секунду:
Trigger usage_2014_06_upsert: time=610256.635 calls=887272
...
Рейтинг: 0 / 0
25 сообщений из 32, страница 1 из 2
Форумы / PostgreSQL [игнор отключен] [закрыт для гостей] / Пакетное добавление/обновление данных
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]