LINUX.ORG.RU

PGSQL Select or Insert function


0

0

Необходимо написать функцию, которая бы выдавала ИД записи, если такова существует или бы создавала новую запись и опять же возвращала бы ИД.

Попробовал такой вариант:

CREATE OR REPLACE FUNCTION getMarkId(character varying)
RETURNS integer AS
$BODY$
DECLARE
BEGIN
   IF(SELECT id FROM «Marks» WHERE «name» = $1) THEN
   RETURN id FROM «Marks» WHERE «name» = $1;   
   ELSE
   INSERT INTO «Marks» («name») VALUES ($1) RETURNING id;
   RETURN getMarkId($1);
   END IF;
END;
$BODY$
LANGUAGE 'plpgsql' VOLATILE
COST 100;

Проблема №1:
Тут селект используется два раза + рекурсия, что, наверно, повлияет на производительность, да и выглядит довольно криво.

Проблема №2:
Если запись существует, то всё ок, а если нужно создать новую, то выдаёт такую ошибку:

ERROR: duplicate key value violates unique constraint «Marks_pkey»
CONTEXT: SQL statement «INSERT INTO „Marks“ („name“) VALUES ( $1 )»
PL/pgSQL function «getmarkid» line 6 at SQL statement

Поскольку сам с функциями в постгресе ещё не до конца разобрался, прошу коллективной помощи :)

★★

Чуть-чуть тупанул - в сиквенсе не задал начальное значение для ID. Но всё равно есть друга ошибка:

ERROR: invalid input syntax for type boolean: «405»
CONTEXT: PL/pgSQL function «getmarkid» line 3 at IF
PL/pgSQL function «getmarkid» line 7 at RETURN

(405 - это текущее значение сиквенса)

SaBo ★★
() автор топика
Ответ на: комментарий от SaBo

Ну теперь ошибка ясна 405 - это не булево значение... Вот как бы его преобразовать?

SaBo ★★
() автор топика

Во, в результате получилась такая функция:


CREATE OR REPLACE FUNCTION getMarkId(character varying)
RETURNS integer AS
$BODY$
DECLARE
BEGIN
   IF((SELECT id FROM «Marks» WHERE «name» = $1) IS NOT NULL) THEN
   RETURN id FROM «Marks» WHERE «name» = $1;   
   ELSE
   INSERT INTO «Marks» («name») VALUES ($1);
   RETURN getMarkId($1);
   END IF;
END;
$BODY$
LANGUAGE 'plpgsql' VOLATILE
COST 100;


Полностью рабочая, но как бы её переписать без повторного селекта и рекурсии?

SaBo ★★
() автор топика

1. Делай SELECT INTO _id чтобы два раза не делать один селект( _id - локальная переменная, нужно объявить в блоке DECLARE)

2. Рекурсию делать не нужно, используй последовательности для получения следующего id:

_id = nextval('Marks_id_seq'); /* имя проследователности нужно уточнять*/

...

INSERT INTO «Marks» (id, name) VALUES (_id, $1);

Либо как у тебя написано же (но не до конца):

INSERT INTO «Marks» («name») VALUES ($1) RETURNING id INTO _id;

Последний вариант работать будет только с версии 8.2, если не ошибаюсь.

mikki
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.