6

Is there a way to insert a new record to a table which doesn't have an auto-increment ID without specifically entering the ID. I just want the ID to be lastId+1.

INSERT INTO lists VALUES (id,'KO','SPH', '5') //new id

6
  • 5
    So you want the behavior to be like a sequence ("auto-increment"), without using a sequence. Why not? These things exist to solve problems which you would otherwise easily get into with concurrent inserts. Commented Sep 4, 2013 at 12:52
  • I just want to insert a new record without changing the DB structure... Commented Sep 4, 2013 at 12:59
  • If this is an exceptional case, alright... If this is the normal modus operandi, maybe you should be changing your database structure. Commented Sep 4, 2013 at 13:00
  • The db design is not up to me :) Commented Sep 4, 2013 at 13:02
  • 2
    Then you may have to talk to the person who it is up to. :) Commented Sep 4, 2013 at 13:03

2 Answers 2

18

Don't do that! EVER! Don't even think about doing that!

This WRONG solution may seems (it doesn't) to work for you:

INSERT INTO lists VALUES ((SELECT max(id)+1 FROM lists),'KO','SPH', '5');

BUT, if someone try to insert at the same time as you, you both would get the same id, which will cause an invalid result. You really should use a sequence or some more reliable mechanism (an auxiliary table is common when you can't have holes in the sequence, but it has some drawbacks [it will lock]). You can even use serial data type to make it easier (it creates a sequence underneath):

CREATE TABLE lists(id serial, col2 text, col3 text, ...);
-- If you don't specify "id", it will autogenerate for you:
INSERT INTO lists(col2, col3, ...) VALUES('KO','SPH', ...);
-- You can also specify using DEFAULT (the same as above):
INSERT INTO lists(id, col2, col3, ...) VALUES(DEFAULT, 'KO','SPH', ...);

If you really, really, REALLY, can't create and use a sequence, you can do as the above, but you will have to handle the exception (assuming the id field is PK or UK, and using a read committed transaction), something like that (in PL/pgSQL):

DECLARE
    inserted bool = false;
BEGIN
    WHILE NOT inserted LOOP;
        BEGIN
            INSERT INTO lists
            VALUES ((SELECT coalesce(max(id),0)+1 FROM lists),'KO','SPH', '5');
            inserted = true;
        EXCEPTION
            WHEN unique_violation THEN
                NULL; -- do nothing, just try again
        END;
    END LOOP;
END;

But again, I highly recommend you to avoid it: use a sequence and be happy... =D

Also, I know it is a example, but use explicit columns list on INSERT INTO clause.

Sign up to request clarification or add additional context in comments.

4 Comments

what happens if there is no data and you do max(id) + 1 ?
@maazza: then max would return NULL (and NULL+1 returns NULL), you can simple handle that easily with COALESCE, I'll update the answer to do that
To be fair, the warning's rather melodramatic. Providing there's a key constraint on the id attribute, the worst that will happen is that one of the inserts will fail.
It seems to be non-obvious why this is a really bad choice. E.g. if there are unique constraints on any other column, and you try to insert a duplicate, you'll be creating an infinite loop.
0

I found this solution for myself, without using additional functions. First, I moved the last table ID to the CTE, then made an increment with a window. Visually, this is a slightly more complex solution, but overall quite effective. Example:

;with table_id as (
    select max(id) as id
    from table
)
insert into table
select table_id.id + row_number() over(),
tab.val1,
val2,
val3
from table_id
cross join(
  values
  ('KO', 'SPH', 5),
  ('PQ', 'RTO', 10)
) as tab(val1, val2, val3);

Comments

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.