Как стать автором
Обновить

Комментарии 36

столило ли ради этого писать пост?
стоило :) я этого не знал. теперь знаю. по-моему PROFIT налицо.
Что вернет этот запрос?
INSERT INTO test (name) VALUES ('My Name 1'), ('My Name 2'), ('My Name 3') RETURNING id;
postgres=# INSERT INTO test (name) VALUES ('My Name 1'), ('My Name 2'), ('My Name 3') RETURNING id;
id
— 4
5
6
(3 rows)


Хабрапарсер немного накозявил, но должно быть понятно
Странно… Я в мануале (www.postgresql.org/docs/8.3/interactive/plpgsql-statements.html) прочитал, что:

>> For INSERT/UPDATE/DELETE with RETURNING, PL/pgSQL reports an error for more than one returned row, even when STRICT is not specified. This is because there is no option such as ORDER BY with which to determine which affected row should be returned.

или я не так что-то понял…
я использую версию PostgreSQL 8.3.8 и подобная запись у меня работает
Вроде как с 8.2 появилось.
Работает, можно напимер удаляя по какому то условию узнать id или name удалённых позиций для выдачи пользователю, или записи в лог.
У меня работает только в таком виде:
new_id = db.execute("INSERT INTO users (username, hash) VALUES (u_name, hashh) RETURNING id",).fetchone()
session["user_id"] = new_id

Без fetchone() выдает ошибку
Еще вопрос: в RETURNING id, я так понимаю, что id — это поле в таблице. Как насчет «RETURNING id, name»?
совершенно верно, под id имеется в виду поле в таблице

postgres=# INSERT INTO test (name) VALUES ('My Name') RETURNING id,name;
id | name
----+---------
10 | My Name
(1 row)

INSERT 0 1
>таким ништячком для меня стал элемент синтаксиса INSERT
RTFM ))
Я начал работать с pl/sql совсем недавно и каждый день узнаю такие «ништячки» от старших товарищей. И это приносить удовольствие, когда вместо 3-5 строк получаеш одну
Достаточно было просто прочитать мануал по Постгре. Там об этом все сказано.
Ну как бы на самом деле так и произошло =)
Сорри за оффтоп, но можно ли подобную штуку на MySQL провернуть, может кто подскажет.
насколько я знаю — нет.

можно сделать так: «select last_insert_id()»
а если ключ — не одно autoincrement-поле, а несколько полей?
Несколько полей не могут быть autoincrement, следовательно вы должны знать их значение при вставке.
Туплю :)
Бывает так, что и не знаешь значение (составной ключ, автоинкремент реализован триггером). Можно ли получить действительно вставленное значение?
вообще то получение последнего id при условии что поле имеет тип serial:
select currval('t_table_id_seq');
Да, но ведь это отдельный запрос к БД!
Не совсем корректно. Как уже сказали это еще один запрос — это первое. Из этого же минуса вытекает то, что между инсертом и кьюрвалом может проскочить еще один инсерт. Раз уж вы хотите выбирать так, то наверное лучше получить некствал, и использовать его для инсерта.
Согласен. Моё упущение. Давно с ПГ плотно не работал — подзабыл.
К теме постгре, перед вставкой какого либо значения необходимо проверить его на существование в БД.
Пользуюсь таким шаблоном: Создаю процедуру.

CREATE OR REPLACE FUNCTION «public».«get_server» (varchar, integer) RETURNS integer AS
$body$
DECLARE serv_count integer;
newid bigint;
BEGIN
SELECT servers.id_server INTO newid FROM servers WHERE domain = $1;
IF newid IS NULL THEN
INSERT INTO servers (domain, ip) VALUES ($1, $2);
ELSE
return newid;
END IF;
SELECT lastval() INTO newid;
RETURN newid;
END;
$body$
LANGUAGE 'plpgsql'

теперь легко просто скриптом сделать так
SELECT get_server('habra.ru', 999)
И, если данных в БД нет — они вставяться, а если есть просто произойдет возврат ID записи.
Это удобно, не загромождает исполняемый код.
а как-то так не проще?
INSERT INTO servers (domain, ip) SELECT 'habra.ru', 999 WHERE not exist(SELECT 1 FROM servers WHERE domain = 'habra.ru') LIMIT 1;
А если запись существует? как возвратить текущий ID?
Можно поизголяться еще так

BEGIN;
SELECT servers.id_server FROM servers WHERE domain = 'habra.ru';
SAVEPOINT my_savepoint;
INSERT INTO servers (domain, ip) VALUES ($1, $2) RETURNING id_server;
RELEASE SAVEPOINT my_savepoint;
COMMIT;
В данном случае между select и insert остаётся промежуток, в котором теоретически может возникнуть новая запись из другого потока. Так что для чистоты придётся использовать блокировку или искать другой путь.
Еще можно использовать функцию nextval(), которая возвращает следующее значение счетчика, и потом вставлять это значение в своем запросе.
каждый вызов nextval() прибавляет единичку к сиквенсу вне зависимости от того сделали мы что-то с полученым id или нет.
Это сделано для нормальной работы в транзакциях. Т.е. чтобы небыло неопределенных случаев когда несколько разных транзакций вызывают эту функцию. В любом случае эти пробелы могут случаться и просто если вы отмените транзакцию после вставки данных — никаких отрицательных эффектов они не оказывают.
хочу дополнить, что тем, кто пользуется Hibernate — это не к чему. Хотя тут можно и поспорить. Дело в том, что в Hibernate есть метод save, который вернёт вам id добавленной записи.

например так:
public Integer addComment(Comment comment) {
return (Integer) getHibernateTemplate().save(comment);
}

В данном случае используется Spring'овая обёртка.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории