You can have something in SQL "generate" a next-sequence-value for you, but it is fraught with problems.
What happens if two people want a next-value at the same time?
What happens if you allocate a value to Person-A, and then another (different) value to Person-B but Person-A never saves their record? (this usually doesn't matter ... but if it does you can't do it that way!)
What about performance? Usually the way this works is for the nextval() / pg_get_serial_sequence() functions to either:
Store the next value. The function returns that value and then increments the value that is stored.
or
Look up the maximum value that is in use, add one, and return that (this does NOT work for two people wanting new values at the same time).
By contrast if you allocate the value when you SAVE the record you CAN "Lookup the MAX current value and add one" because the operation is ATOMic - if two people SAVE at the same time they will get in a queue, so the second person's value will be one-more-than-the-first-person's
Downside of ALL of these is that they take more "effort" than the simple solutions. They have to do a database lookup, they have to have sophisticated locking (which itself becomes a bottleneck in a busy system), and they have to do a round-trip to the SQL Server to get the next value and THEN another round trip to actually save the record.
(There is one area where this type of solution excels, and that is where your nextval() function has an extra parameter - the number of rows you will be creating. SO if you routinely add more than one row in a batch the nextval() function returns the first ID number you should use and then reserves the next N-values for you. Basically you say you want 10 values and nextval() gets the current value and increments it by 10.
But for one-by-one record creation it is not a good solution.
The better solution is to allocate the value when you save the record. In particular using the IDENTITY property, because SQL manages the "what is the next value" stuff for you. You don;t have to worry about two people getting the same value etc etc. The only, possible, downside is that if you do:
INSERT INTO YourTable ...
... do some other stuff ...
ROLLBACK
then the IDENTITY value that SQL gave YOU will then NOT be used by someone else. They will get the next-number-AFTER the one you were given (but didn't use). Thus you can get gaps in your ID numbers. Best to learn to live with that, rather than try to "fill in the gaps".
But ... that does NOT work well for batches of numbers (yeah SQL will happily allocate a unique, ascending, value for each row on a column that has the IDENTITY property if you INSERT a whole batch of rows, but it is much harder to send that information back to the caller so they can then insert some associated records in another table, using the newly allocated ID numbers from the Primary Table). For a single row its easy, for a multi-row-batch it is harder.
There are two ways to do that.
- Just SELECT it and "read" the resultset you get back. So your application does:
INSERT INTO YourTable(Col1, Col2) VALUES(123, 'abc')
SELECT scope_identity() AS [ThisIsYourID]
then you just interrogate the value of "ThisIsYourID" from the resultset you get back.
- Use an OUTPUT parameter
Call a Stored Procedure to do the actual insert. That can store the result from scope_identity() and pass it back to you in one of the parameters that you called the SProc with:
CREATE PROCEDURE SaveMyRecord
@Col1 int,
@Col2 varchar(10)
@TheAllocatedID int OUTPUT
AS
INSERT INTO YourTable(Col1, Col2) VALUES(@Col1, @Col2)
SELECT @TheAllocatedID = scope_identity()
If you don't want to write Stored Procedures (i.e. you are using dynamically generated SQL in your APP) you can get the same effect using sp_ExecuteSQL:
EXEC sp_ExecuteSQL N'INSERT INTO YourTable(Col1, Col2) VALUES(123, ''abc'')
SELECT @TheAllocatedID = scope_identity()',
N'@TheAllocatedID int OUTPUT',
@TheAllocatedID = @TheAllocatedID OUTPUT
But if you want an Array of allocated-IDs sent back to your APP its all a lot harder.