Возврат нескольких значений SERIAL из пакетной вставки Posgtres - PullRequest
6 голосов
/ 04 мая 2011

Я работаю с Postgres, используя SERIAL в качестве основного ключа.После вставки строки я могу получить сгенерированный ключ, используя 'RETURNING' или CURRVAL().

. Теперь моя проблема заключается в том, что я хочу выполнить пакетную вставку внутри транзакции и получить ВСЕ сгенерированныеключи.

Все, что я получаю с RETURNING и CURRVAL, является последним сгенерированным идентификатором, остальная часть результата отбрасывается.

Как я могу заставить его вернуть все из них?

Спасибо

Ответы [ 5 ]

20 голосов
/ 04 мая 2011

Вы можете использовать RETURNING с несколькими значениями:

psql=> create table t (id serial not null, x varchar not null);
psql=> insert into t (x) values ('a'),('b'),('c') returning id;
 id 
----
  1
  2
  3
(3 rows)

Итак, вы хотите что-то более похожее на это:

INSERT INTO AutoKeyEntity (Name,Description,EntityKey) VALUES
('AutoKey 254e3c64-485e-42a4-b1cf-d2e1e629df6a','Testing 5/4/2011 8:59:43 AM',DEFAULT)
returning EntityKey;
INSERT INTO AutoKeyEntityListed (EntityKey,Listed,ItemIndex) VALUES
(CURRVAL('autokeyentity_entityKey_seq'),'Test 1 AutoKey 254e3c64-485e-42a4-b1cf-d2e1e629df6a', 0),
(CURRVAL('autokeyentity_entityKey_seq'),'Test 2 AutoKey 254e3c64-485e-42a4-b1cf-d2e1e629df6a', 1),
(CURRVAL('autokeyentity_entityKey_seq'),'Test 3 AutoKey 254e3c64-485e-42a4-b1cf-d2e1e629df6a', 2)
returning EntityKey;
-- etc.

И тогда вам придется собрать возвращенные EntityKey значений из каждого оператора в вашей транзакции.

Вы можете попытаться получить текущее значение последовательности в начале и конце транзакции и использовать их, чтобы выяснить, какие значения последовательности использовались, но , то естьненадежно :

Кроме того, хотя несколько сеансов гарантированно выделяют различные значения последовательности, значения могут быть сгенерированы вне последовательности, когда рассматриваются все сеансы.Например, при значении cache , равном 10, сеанс A может зарезервировать значения 1..10 и вернуть nextval=1, тогда сеанс B может зарезервировать значения 11..20 и вернуть nextval=11 до того, как сеанс Aгенерируется nextval = 2.Таким образом, при значении cache , равном единице, можно предположить, что значения nextval генерируются последовательно;при значении cache больше единицы вы должны предполагать, что все значения nextval различны, а не генерируются чисто последовательно.Кроме того, last_value будет отражать последнее значение, зарезервированное для любого сеанса, независимо от того, было ли оно еще возвращено nextval.

Таким образом, даже если ваши последовательности имеют кеш значения одного вы можете иметь несмежные значения последовательности в вашей транзакции.Тем не менее, вы можете быть в безопасности, если значение cache последовательности соответствует количеству INSERT в вашей транзакции, но я предполагаю, что оно будет слишком большим, чтобы иметь смысл.

ОБНОВЛЕНИЕ : Я только что заметил (благодаря комментариям спрашивающего), что в нем задействованы две таблицы, они немного потерялись в стене текста.

В этом случае вы сможете использовать текущийINSERTS:

INSERT INTO AutoKeyEntity (Name,Description,EntityKey) VALUES
('AutoKey 254e3c64-485e-42a4-b1cf-d2e1e629df6a','Testing 5/4/2011 8:59:43 AM',DEFAULT)
returning EntityKey;
INSERT INTO AutoKeyEntityListed (EntityKey,Listed,ItemIndex) VALUES
(CURRVAL('autokeyentity_entityKey_seq'),'Test 1 AutoKey 254e3c64-485e-42a4-b1cf-d2e1e629df6a', 0),
(CURRVAL('autokeyentity_entityKey_seq'),'Test 2 AutoKey 254e3c64-485e-42a4-b1cf-d2e1e629df6a', 1),
(CURRVAL('autokeyentity_entityKey_seq'),'Test 3 AutoKey 254e3c64-485e-42a4-b1cf-d2e1e629df6a', 2);
-- etc.

И получить значения EntityKey по одному из INSERT на AutoEntityKey.Для обработки значений RETURNING может потребоваться какой-то сценарий.Вы также можете обернуть AutoKeyEntity и связанные AutoKeyEntityListed INSERT в функцию, а затем использовать INTO, чтобы получить значение EntityKey и вернуть его из функции:

INSERT INTO AutoKeyEntity /*...*/ RETURNING EntityKey INTO ek;
/* AutoKeyEntityListed INSERTs ... */
RETURN ek;
4 голосов
/ 08 мая 2011

Вы можете предварительно назначить последовательные идентификаторы, используя это:

SELECT setval(seq, nextval(seq) + num_rows - 1, true) as stop

это должна быть более быстрая альтернатива звонку nextval() миллиардов раз.

вы также можете хранить идентификаторы во временной таблице:

create temporary blah (
  id int
) on commit drop;

insert into table1 (...) values (...)
returning id into blah;

в Postgres 9.1, может использовать CTE:

with
ids as (
insert into table1 (...) values (...)
    returning id
)
insert into table2 (...)
select ...
from ids;
3 голосов
/ 04 мая 2011

В вашем приложении соберите значения из последовательности:

SELECT nextval( ... ) FROM generate_series( 1, number_of_values ) n

Создайте строки, используя эти значения, и просто вставьте (используя многострочную вставку). Это безопасно (SERIAL работает, как и следовало ожидать, без повторного использования значений, одновременное подтверждение и т. Д.) И быстро (вы вставляете все строки одновременно без большого количества циклических обращений клиент-сервер).

1 голос
/ 04 мая 2011

Отвечая на комментарий Скотта Марлоу более подробно:

Допустим, у вас есть древовидная таблица с обычной ссылкой на parent_id, и вы хотите импортировать большое дерево записей.Проблема в том, что вам нужно, чтобы значение PK родителя было известно, чтобы вставить дочерние элементы, поэтому потенциально для этого может потребоваться множество отдельных операторов INSERT.

Таким образом, решение может быть следующим:

  • buildдерево в приложении
  • захватывает столько значений последовательности, сколько узлов для вставки, используя «SELECT nextval (...) FROM generate_series (1, number_of_values) n» (порядок значений не имеет значения)
  • присваивает эти значения первичного ключа узлам
  • выполняет массовую вставку (или COPY), пересекая древовидную структуру, поскольку PK, используемые для отношений, известны
0 голосов
/ 04 мая 2011

Есть три способа сделать это. Используйте currval (), используйте return или напишите сохраненную процедуру, чтобы обернуть любой из этих методов в симпатичное маленькое одеяло, которое не позволяет вам делать все это в половине клиентской половины postgres.

Currval method:
begin;
insert into table a (col1, col2) values ('val1','val2');
select currval('a_id_seq');
123  -- returned value
-- client code creates next statement with value from select currval
insert into table b (a_fk, col3, col4) values (123, 'val3','val4');
-- repeat the above as many times as needed then...
commit;

Returning method:
begin;
insert into table a (col1, col2) values ('val1','val2'), ('val1','val2'), ('val1','val2') returning a_id; -- note we inserted three rows
123  -- return values
124
126
insert into table b (a_fk, col3, col4) values (123, 'val3','val4'), (124, 'val3','val4'), (126, 'val3','val4');
commit;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...