8

I'm trying to wrap all my transactions that should be all-or-nothing into BEGIN and COMMIT, but I'm not sure how to do this in cases like the following.

I have 3 tables, one for images, one for albums, and one for the relations between them, namely album_images. The way the system works is that a user can create an album and fill it with his images in one operation. The SQL is as follows:

BEGIN;
  INSERT INTO albums [...];  -- Create a new album row
  SELECT id AS album_id FROM albums WHERE [...];  -- Get that rows ID
  -- Now use album_id in the next statement
  INSERT INTO album_images (album_id, image_id) [...];
COMMIT;

This is probably a common problem, I'm just not sure what to search for and I can't seem to find a solution in the documentation either.

4 Answers 4

9

As an alternative to the INSERT ... RETURNING clause mentioned by Cody, you can use the current value the sequence associated with the ID column:

BEGIN;
  INSERT INTO albums [...];
  INSERT INTO album_images (currval('albums_id_seq'), image_id) [...];
COMMIT;

This assumes the standard naming scheme from Postgres when creating the sequence automatically for columns defined as serial.

Another alternative - if you are only using a single insert - is to use the lastval() function. You would then not even need to put the sequence name into the INSERT statement:

BEGIN;
  INSERT INTO albums [...];
  INSERT INTO album_images (lastval(), image_id) [...];
COMMIT;
Sign up to request clarification or add additional context in comments.

12 Comments

Hm, both examples produce syntax error at or near "(" for me. Even if one of these will work eventually, I take it there is no way of using the value from album_id from my SELECT?
Is lastval specific to the transaction, or could it be altered by another concurrent transaction?
It's specific to the connection (=session)
@a_horse_with_no_name so it could return the wrong value if another (sharing the connection) inserted concurrently
@TobiAkinyemi: your assumption is wrong. Using lastval() directly after an INSERT is safe, just as using currval() is. If you have an application where it's possible that some code does an insert and another part does a different insert using the same physical connect before the first transaction gets to call lastval() then I would argue that your code is broken. But even in that situation currval() would be safe assuming that both inserts use different tables.
|
3

Manually accessing sequences is not ideal

BEGIN;
  -- Create a new album row and hold onto the id
  WITH row as (INSERT INTO albums [...] RETURNING id AS album_id)
  -- Now use album_id in the next insert
  INSERT INTO album_images SELECT album_id, :image_id FROM row;
COMMIT;

returning was actually useful, however, the other 2 answers didn't know/explain how to apply it here

3 Comments

"Manually accessing sequences is not a robust solution" - yes it is.
@a_horse_with_no_name changed my verbiage
You need to at least remove the ; after the CTE to make that a correct statement.
1

Postgres provides a "RETURNING" clause used in an INSERT to easily get back the newly-created primary key.

http://www.postgresql.org/docs/8.3/interactive/sql-insert.html

Some examples: http://6c62.net/blog/?p=48

2 Comments

Thanks, but my problem isn't so much with getting that ID, but with using it in the next statement. That SELECT in my example is actual working code that will return the correct ID. Or did I misunderstand something? Those examples are a little over my head.
When i try to use a named RETURNING inside another statement of the transaction it throw an "unknow column" errror... I dont think that thoses variables are shared within the transaction.
-2

You can get the id of the inserted row as

INSERT INTO albums [...] returning album_id;

This will return the album_id after insertion.

1 Comment

Specifically, the OP want's to use the id in the next statement in the transaction. RETURNING clause does not solve this independently.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.