简体   繁体   中英

How to to get the value of an auto increment column in postgres from a .sql script file?

In postgres I have two tables like so

CREATE TABLE foo ( 
    pkey   SERIAL PRIMARY KEY,
    name   TEXT
);

CREATE TABLE bar (
   pkey SERIAL PRIMARY KEY,
   foo_fk INTEGER REFERENCES foo(pkey) NOT NULL,
   other  TEXT
 ); 

What I want to do is to write a .sql script file that does the following

INSERT INTO foo(name) VALUES ('A') RETURNING pkey AS abc; 
INSERT INTO bar(foo_fk,other) VALUES 
               (abc, 'other1'),
               (abc, 'other2'),
               (abc, 'other3');

which produces the error below in pgAdmin

Query result with 1 row discarded.
ERROR:  column "abc" does not exist
LINE 3:                    (abc, 'other1'),

********** Error **********

ERROR: column "abc" does not exist
SQL state: 42703
Character: 122

Outside of a stored procedure how do a define a variable that I can use between statements? Is there some other syntax for being able to insert into bar with the pkey returned from the insert to foo.

You can combine the queries into one. Something like:

with foo_ins as (INSERT INTO foo(name) 
                 VALUES ('A') 
                 RETURNING pkey AS foo_id)
INSERT INTO bar(foo_fk,other)
SELECT foo_id, 'other1' FROM foo_ins
UNION ALL
SELECT foo_id, 'other2' FROM foo_ins
UNION ALL
SELECT foo_id, 'other3' FROM foo_ins;

Other option - use an anonymous PL/pgSQL block like:

DO $$
DECLARE foo_id INTEGER;
BEGIN
    INSERT INTO foo(name) 
    VALUES ('A') 
    RETURNING pkey INTO foo_id;

    INSERT INTO bar(foo_fk,other) 
    VALUES         (foo_id, 'other1'),
                   (foo_id, 'other2'),
                   (foo_id, 'other3');
END$$;

You can use lastval() to ...

Return the value most recently returned by nextval in the current session.

This way you do not need to know the name of the seqence used.

INSERT INTO foo(name) VALUES ('A');
INSERT INTO bar(foo_fk,other) VALUES 
  (lastval(), 'other1')
, (lastval(), 'other2')
, (lastval(), 'other3')
;

This is safe because you control what you called last in your own session.


If you use a writable CTE as proposed by @Ihor , you can still use a short VALUES expression in the 2nd INSERT . Combine it with a CROSS JOIN (or append the CTE name after a comma ( , ins ) - same thing):

WITH ins AS (
   INSERT INTO foo(name) 
   VALUES ('A') 
   RETURNING pkey
   )
INSERT INTO bar(foo_fk, other)
SELECT ins.pkey, o.other 
FROM  (
   VALUES
      ('other1'::text)
    , ('other2')
    , ('other3')
   ) o(other)
CROSS  JOIN ins;

Another option is to use currval

INSERT INTO foo
  (name) 
VALUES 
  ('A') ; 

INSERT INTO bar
  (foo_fk,other) 
VALUES 
  (currval('foo_pkey_seq'), 'other1'),
  (currval('foo_pkey_seq'), 'other2'),
  (currval('foo_pkey_seq'), 'other3');

The automatically created sequence for serial columns is always named <table>_<column>_seq

Edit :

A more "robust" alternative is to use pg_get_serial_sequence as Igor pointed out.

INSERT INTO bar
  (foo_fk,other) 
VALUES 
  (currval(pg_get_serial_sequence('public.foo', 'pkey')), 'other1'),
  (currval(pg_get_serial_sequence('public.foo', 'pkey')), 'other2'),
  (currval(pg_get_serial_sequence('public.foo', 'pkey')), 'other3');

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM