AutoIncrement fields on databases without autoincrement field - sql

In MS Sql Server is easy create autoincrement fields. In my systems I stopped to use autoincrement fields for primary keys, and now I use Guid's. It was awesome, I've got a lot of advantages with that change. But in another non-primary key fields, I really was needing implement a "soft autoincrement". It's because my system is DB independent, so I create the autoinc value programatically in c#.
I would like about solutions for autoincrement fields on databases without autoincrement, what the solution that your use and why? There is some Sql Ansi statement about this? and generating directly from my c#, is a better solution?
PS: I know that select max(id)+1 from table it's not really concurrent friendly...

The mechanism to generate unique id values must not be subject to transaction isolation. This is required for the database to generate a distinct value for each client, better than the trick of SELECT MAX(id)+1 FROM table, which results in a race condition if two clients try to allocate new id values concurrently.
You can't simulate this operation using standard SQL queries (unless you use table locks or serializable transactions). It has to be a mechanism built into the database engine.
ANSI SQL did not describe an operation to generate unique values for surrogate keys until SQL:2003. Before that, there was no standard for auto-incrementing columns, so nearly every brand of RDBMS provided some proprietary solution. Naturally they vary a lot, and there's no way to use them in a simple, database-independent manner.
MySQL has the AUTO_INCREMENT column option, or SERIAL pseudo-datatype which is equivalent to BIGINT UNSIGNED AUTO_INCREMENT;
Microsoft SQL Server has the IDENTITY column option and NEWSEQUENTIALID() which is something between auto-increment and GUID;
Oracle has a SEQUENCE object;
PostgreSQL has a SEQUENCE object, or SERIAL pseudo-datatype which implicitly creates a sequence object according to a naming convention;
InterBase/Firebird has a GENERATOR object which is pretty much like a SEQUENCE in Oracle; Firebird 2.1 supports SEQUENCE too;
SQLite treats any integer declared as your primary key as implicitly auto-incrementing;
DB2 UDB has just about everything: SEQUENCE objects, or you can declare columns with the "GEN_ID" option.
All these mechanisms operate outside transaction isolation, ensuring that concurrent clients get unique values. Also in all cases there is a way to query the most recently generated value for your current session. There has to be, so you can use it to insert rows in a child table.

I think your question is actually quite a good one. However, it is easy to get lost trying to come up with a SQL only solution. In reality you will want the optimization and transaction safety afforded by using the database implementations of the autoincrement types.
If you need to abstract out the implementation of the autoincrement operator, why not create a stored procedure to return your autoincrement value. Most SQL dialects access stored procedures in relatively the same way and it should be more portable. Then you can create database specific autoincrement logic when you create the sproc - eliminating the need to change many statements to be vendor specific.
Done this way, your inserts could be as simple as:
INSERT INTO foo (id, name, rank, serial_number)
VALUES (getNextFooId(), 'bar', 'fooRank', 123456);
Then define getNextFooId() in a database specific way when the database is being initialized.

Most databases that don't have autoincrement fields like SQL Server (I'm thinking Oracle specifically) have sequences where you ask the Sequence for the next number. No matter how many people are requesting numbers at the same time everyone gets a unique number.

The traditional solution is to have a table of ids that look something like this
CREATE TABLE ids (
tablename VARCHAR(32) NOT NULL PRIMARY KEY,
nextid INTEGER
)
which s populated with one row per table when you create the database.
You then do a select to get the next next id for the table you are inserting into, increment it and then update the table with the new id. Obviously, there are locking issues here, but for databases with moderate insert rates it works well. And it is completely portable.

If you need a non-primary-key autoincrement field, a very nice MySQL only solution for creating arbitraty sequences is to use the relatively unknown last_insert_id(expr) function.
If expr is given as an argument to
LAST_INSERT_ID(), the value of the
argument is returned by the function
and is remembered as the next value to
be returned by LAST_INSERT_ID(). This
can be used to simulate sequences...
(from http://dev.mysql.com/doc/refman/5.1/en/information-functions.html#function_last-insert-id )
Here is an example which demonstrates how a secondary sequence can be kept for numbering comments for each post:
CREATE TABLE `post` (
`id` INT(10) UNSIGNED NOT NULL,
`title` VARCHAR(100) NOT NULL,
`comment_sequence` INT(10) UNSIGNED NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
);
CREATE TABLE `comment` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`post_id` INT(10) UNSIGNED NOT NULL,
`sequence` INT(10) UNSIGNED NOT NULL,
`content` TEXT NOT NULL,
PRIMARY KEY (`id`)
);
INSERT INTO post(id, title) VALUES(1, 'first post');
INSERT INTO post(id, title) VALUES(2, 'second post');
UPDATE post SET comment_sequence=Last_insert_id(comment_sequence+1) WHERE id=1;
INSERT INTO `comment`(post_id, sequence, content) VALUES(1, Last_insert_id(), 'blah');
UPDATE post SET comment_sequence=Last_insert_id(comment_sequence+1) WHERE id=1;
INSERT INTO `comment`(post_id, sequence, content) VALUES(1, Last_insert_id(), 'foo');
UPDATE post SET comment_sequence=Last_insert_id(comment_sequence+1) WHERE id=1;
INSERT INTO `comment`(post_id, sequence, content) VALUES(1, Last_insert_id(), 'bar');
UPDATE post SET comment_sequence=Last_insert_id(comment_sequence+1) WHERE id=2;
INSERT INTO `comment`(post_id, sequence, content) VALUES(2, Last_insert_id(), 'lorem');
UPDATE post SET comment_sequence=Last_insert_id(comment_sequence+1) WHERE id=2;
INSERT INTO `comment`(post_id, sequence, content) VALUES(2, Last_insert_id(), 'ipsum');
SELECT * FROM post;
SELECT * FROM comment;

Related

What different between SERIAL and INT Generated Always As Identity in PostgreSQL? [duplicate]

To have an integer auto-numbering primary key on a table, you can use SERIAL
But I noticed the table information_schema.columns has a number of identity_ fields, and indeed, you could create a column with a GENERATED specifier...
What's the difference? Were they introduced with different PostgreSQL versions? Is one preferred over the other?
serial is the "old" implementation of auto-generated unique values that has been part of Postgres for ages. However that is not part of the SQL standard.
To be more compliant with the SQL standard, Postgres 10 introduced the syntax using generated as identity.
The underlying implementation is still based on a sequence, the definition now complies with the SQL standard. One thing that this new syntax allows is to prevent an accidental override of the value.
Consider the following tables:
create table t1 (id serial primary key);
create table t2 (id integer primary key generated always as identity);
Now when you run:
insert into t1 (id) values (1);
The underlying sequence and the values in the table are not in sync any more. If you run another
insert into t1 default_values;
You will get an error because the sequence was not advanced by the first insert, and now tries to insert the value 1 again.
With the second table however,
insert into t2 (id) values (1);
Results in:
ERROR: cannot insert into column "id"
Detail: Column "id" is an identity column defined as GENERATED ALWAYS.
So you can't accidentally "forget" the sequence usage. You can still force this, using the override system value option:
insert into t2 (id) overriding system value values (1);
which still leaves you with a sequence that is out-of-sync with the values in the table, but at least you were made aware of that.
identity columns also have another advantage: they also minimize the grants you need to give to a role in order to allow inserts.
While a table using a serial column requires the INSERT privilege on the table and the USAGE privilege on the underlying sequence this is not needed for tables using an identity columns. Granting the INSERT privilege is enough.
It is recommended to use the new identity syntax rather than serial

Add serial value across multiple tables

I have the following two tables in my Postgres database:
CREATE TABLE User (
Id serial UNIQUE NOT NULL,
Login varchar(80) UNIQUE NOT NULL,
PRIMARY KEY (Id,Login)
);
CREATE TABLE UserData (
Id serial PRIMARY KEY REFERENCES Users (Id),
Password varchar(255) NOT NULL
);
Say, I add a new user with INSERT INTO Users(Id, Login) VALUES(DEFAULT, 'John') and also want to add VALUES(id, 'john1980') in UserData where id is John's new id.
How do I get that id? Running a query for something just freshly created seems superfluous. I have multiple such situations across the database. Maybe my design is flawed in general?
(I'm obviously not storing passwords like that.)
1) Fix your design
CREATE TABLE usr (
usr_id serial PRIMARY KEY,
,login text UNIQUE NOT NULL
);
CREATE TABLE userdata (
usr_id int PRIMARY KEY REFERENCES usr
,password text NOT NULL
);
Start by reading the manual about identifiers and key words.
user is a reserved word. Never use it as identifier.
Use descriptive identifiers. id is useless.
Avoid mixed case identifiers.
serial is meant for a unique column that can be pk on its own. No need for a multicolumn pk.
The referencing column userdata.usr_id cannot be a serial, too. Use a plain integer.
I am just using text instead of varchar(n), that's optional. More here.
You might consider to merge the two tables into one ...
2) Query to INSERT in both
Key is the RETURNING clause available for INSERT, UPDATE, DELETE, to return values from the current row immediately.
Best use in a data-modifying CTE:
WITH ins1 AS (
INSERT INTO usr(login)
VALUES ('John') -- just omit default columns
RETURNING usr_id -- return automatically generated usr_id
)
INSERT INTO userdata (usr_id, password )
SELECT i.usr_id, 'john1980'
FROM ins1 i;
You can consider using a trigger. The Id column of the newly inserted row can be accessed by the name NEW.Id.
References:
CREATE TRIGGER documentation on PostgreSQL Manual
Trigger Procedures

SQL Oracle Determining Primary Key Values

In Oracle SQL what is the best way to create primary key values for an entity? I have been adding 100 for each different entity and incrementing new entities by 1, but I can see how this is not good because if I have over 100 inserts into a table I would reuse a primary key number. I have many tables with primary keys, how do I determine a way to make sure all of the values are unique and there is no chance of them overlapping with other primary key values?
An example of what I have been doing is as follows:
create table example (
foo_id number(5);
Constraint example_foo_id_pk Primary key (foo_id);
Insert Into example
Values(2000);
Insert Into example
Values(2010);
create table example2 (
foobar_id number(5);
Constraint example2_foobar_id_pk Primary key (foobar_id);
Insert Into example2
Values (2100);
Insert Into example2
Values (2110);
In Oracle people commonly use sequences to generate numbers. In an insert trigger, the next value of the sequence is queried and put in the primary key field. So you normally don't pass a value for that field yourself.
Something like this:
CREATE SEQUENCE seq_example;
CREATE OR REPLACE TRIGGER tib_example
BEFORE INSERT ON example
FOR EACH ROW
BEGIN
SELECT seq_example .NEXTVAL
INTO :new.foo_id
FROM dual;
END;
/
Then you can just insert a record without passing any value for the id, only for the other fields.
If you want the keys to be unique over multiple tables, you can use the same sequence for each of them, but usually this is not necessary at all. A foo and a bar can have the same numeric id if they are different entities.
If you want every entity to have a unique ID throughout your database, you might consider using GUIDs.
Try using a sequence..
CREATE SEQUENCE Seq_Foo
MINVALUE 1
MAXVALUE 99999999
START WITH 1
INCREMENT BY 1;
To use the sequence in an insert, use Seq_Foo.NextVal.
Starting with Oracle database 12C, you can use identity columns. Use something like
foobar_id number(5) GENERATED BY DEFAULT ON NULL AS IDENTITY
For older versions sequences are the recommended way, although some ORM tools offer using a table which stores the counter. Inserting via sequence can be done either with triggers or by directly inserting sequence.nnextval into your table. The latter may be useful if you need the generated ID for other purposes (like inserting into child tables).

retrieve latest primary key of table to use as foreign key in different table

I'm using sqlite3 and would like to know, if there is a fast way to retrieve the primary key created during the last insert statement in table A, so I can use it as foreign key for different insert statement in table B. For example, first add a new car brand in one table, then add some models of the brand in a different table.
TABLE car_brands (
brandID INTEGER PRIMARY KEY NOT NULL,
name TEXT
)
TABLE car_models (
modelID INTEGER PRIMARY KEY NOT NULL,
brandID INTEGER REFERENCES brandID,
name TEXT
)
sqlite3_last_insert_rowid() is no option, since I do not know if another thread is updating the database during this operation.
Will I have to retrieve it using a separate statement?
Greetings,
curiosity
According to the online help, both sqlite3_last_insert_rowid() and last_insert_rowid() (for use within SQL) are connection specific.
As long as you ensure multiple threads are not sharing the same connection, you won't get concurrency issues with sqlite3_last_insert_rowid().
And within your SQL statements, one connection can only be running one query. Provided you have both the INSERT and the call to last_insert_rowid() within the same batch, that shouldn't have concurrency issues either.

Is there AUTO INCREMENT in SQLite?

I am trying to create a table with an auto-incrementing primary key in Sqlite3. I am not sure if this is really possible, but I am hoping to only have to designate the other fields.
For example:
CREATE TABLE people (id integer primary key auto increment, first_name varchar(20), last_name varchar(20));
Then, when I add a value, I was hoping to only have to do:
INSERT INTO people
VALUES ("John", "Smith");
Is this even possible?
I am running sqlite3 under cygwin in Windows 7.
You get one for free, called ROWID. This is in every SQLite table whether you ask for it or not.
If you include a column of type INTEGER PRIMARY KEY, that column points at (is an alias for) the automatic ROWID column.
ROWID (by whatever name you call it) is assigned a value whenever you INSERT a row, as you would expect. If you explicitly assign a non-NULL value on INSERT, it will get that specified value instead of the auto-increment. If you explicitly assign a value of NULL on INSERT, it will get the next auto-increment value.
Also, you should try to avoid:
INSERT INTO people VALUES ("John", "Smith");
and use
INSERT INTO people (first_name, last_name) VALUES ("John", "Smith");
instead. The first version is very fragile — if you ever add, move, or delete columns in your table definition the INSERT will either fail or produce incorrect data (with the values in the wrong columns).
Yes, this is possible. According to the SQLite FAQ:
A column declared INTEGER PRIMARY KEY will autoincrement.
As of today — June 2018
Here is what official SQLite documentation has to say on the subject (bold & italic are mine):
The AUTOINCREMENT keyword imposes extra CPU, memory, disk space, and
disk I/O overhead and should be avoided if not strictly needed. It is
usually not needed.
In SQLite, a column with type INTEGER PRIMARY KEY is an alias for the
ROWID (except in WITHOUT ROWID tables) which is always a 64-bit signed
integer.
On an INSERT, if the ROWID or INTEGER PRIMARY KEY column is not
explicitly given a value, then it will be filled automatically with an
unused integer, usually one more than the largest ROWID currently in
use. This is true regardless of whether or not the AUTOINCREMENT
keyword is used.
If the AUTOINCREMENT keyword appears after INTEGER PRIMARY KEY, that
changes the automatic ROWID assignment algorithm to prevent the reuse
of ROWIDs over the lifetime of the database. In other words, the
purpose of AUTOINCREMENT is to prevent the reuse of ROWIDs from
previously deleted rows.
SQLite AUTOINCREMENT is a keyword used for auto incrementing a value of a field in the table. We can auto increment a field value by using AUTOINCREMENT keyword when creating a table with specific column name to auto incrementing it.
The keyword AUTOINCREMENT can be used with INTEGER field only.
Syntax:
The basic usage of AUTOINCREMENT keyword is as follows:
CREATE TABLE table_name(
column1 INTEGER AUTOINCREMENT,
column2 datatype,
column3 datatype,
.....
columnN datatype,
);
For Example See Below:
Consider COMPANY table to be created as follows:
sqlite> CREATE TABLE TB_COMPANY_INFO(
ID INTEGER PRIMARY KEY AUTOINCREMENT,
NAME TEXT NOT NULL,
AGE INT NOT NULL,
ADDRESS CHAR(50),
SALARY REAL
);
Now, insert following records into table TB_COMPANY_INFO:
INSERT INTO TB_COMPANY_INFO (NAME,AGE,ADDRESS,SALARY)
VALUES ( 'MANOJ KUMAR', 40, 'Meerut,UP,INDIA', 200000.00 );
Now Select the record
SELECT *FROM TB_COMPANY_INFO
ID NAME AGE ADDRESS SALARY
1 Manoj Kumar 40 Meerut,UP,INDIA 200000.00
Have you read this? How do I create an AUTOINCREMENT field.
INSERT INTO people
VALUES (NULL, "John", "Smith");
Always insert NULL as the id.
One should not specify AUTOINCREMENT keyword near PRIMARY KEY.
Example of creating autoincrement primary key and inserting:
$ sqlite3 ex1
CREATE TABLE IF NOT EXISTS room(room_id INTEGER PRIMARY KEY, name VARCHAR(25) NOT NULL, home_id VARCHAR(25) NOT NULL);
INSERT INTO room(name, home_id) VALUES ('test', 'home id test');
INSERT INTO room(name, home_id) VALUES ('test 2', 'home id test 2');
SELECT * FROM room;
will give:
1|test|home id test
2|test 2|home id test 2
Beside rowid, you can define your own auto increment field but it is not recommended. It is always be better solution when we use rowid that is automatically increased.
The AUTOINCREMENT keyword imposes extra CPU, memory, disk space, and
disk I/O overhead and should be avoided if not strictly needed. It is
usually not needed.
Read here for detailed information.
What you do is correct, but the correct syntax for 'auto increment' should be without space:
CREATE TABLE people (id integer primary key autoincrement, first_name string, last_name string);
(Please also note that I changed your varchars to strings. That's because SQLite internally transforms a varchar into a string, so why bother?)
then your insert should be, in SQL language as standard as possible:
INSERT INTO people(id, first_name, last_name) VALUES (null, 'john', 'doe');
while it is true that if you omit id it will automatically incremented and assigned, I personally prefer not to rely on automatic mechanisms which could change in the future.
A note on autoincrement: although, as many pointed out, it is not recommended by SQLite people, I do not like the automatic reuse of ids of deleted records if autoincrement is not used. In other words, I like that the id of a deleted record will never, ever appear again.
HTH
I know this answer is a bit late. My purpose for this answer is for everyone's reference should they encounter this type of challenge with SQLite now or in the future and they're having a hard time with it.
Now, looking back at your query, it should be something like this.
CREATE TABLE people (id integer primary key autoincrement, first_name varchar(20), last_name varchar(20));
It works on my end. Like so,
Just in case you are working with SQLite, I suggest for you to check out DB Browser for SQLite. Works on different platforms as well.