SQLite auto-increment non-primary key field - sql

Is it possible to have a non-primary key to be auto-incremented with every insertion?
For example, I want to have a log, where every log entry has a primary key (for internal use), and a revision number ( a INT value that I want to be auto-incremented).
As a workaround, this could be done with a sequence, yet I believe that sequences are not supported in SQLite.

You can do select max(id)+1 when you do the insertion.
For example:
INSERT INTO Log (id, rev_no, description)
VALUES ((SELECT MAX(id) + 1 FROM log), 'rev_Id', 'some description')
Note that this will fail on an empty table since there won't be a record with id is 0 but you can either add a first dummy entry or change the sql statement to this:
INSERT INTO Log (id, rev_no, description)
VALUES ((SELECT IFNULL(MAX(id), 0) + 1 FROM Log), 'rev_Id', 'some description')

SQLite creates a unique row id (rowid) automatically. This field is usually left out when you use "select * ...", but you can fetch this id by using "select rowid,* ...". Be aware that according to the SQLite documentation, they discourage the use of autoincrement.
create table myTable ( code text, description text );
insert into myTable values ( 'X', 'some descr.' );
select rowid, * from myTable;
:: Result will be;
1|X|some descr.
If you use this id as a foreign key, you can export rowid - AND import the correct value in order to keep data integrity;
insert into myTable values( rowid, code text, description text ) values
( 1894, 'X', 'some descr.' );

You could use a trigger (http://www.sqlite.org/lang_createtrigger.html) that checks the previous highest value and then increments it, or if you are doing your inserts through in a stored procedure, put that same logic in there.

My answer is very similar to Icarus's so I no need to mention it.
You can use Icarus's solution in a more advanced way if needed. Below is an example of seat availiabilty table for a train reservation system.
insert into Availiability (date,trainid,stationid,coach,seatno)
values (
'11-NOV-2013',
12076,
'SRR',
1,
(select max(seatno)+1
from Availiability
where date='11-NOV-2013'
and trainid=12076
and stationid='SRR'
and coach=1)
);

You can use an AFTER INSERT trigger to emulate a sequence in SQLite (but note that numbers might be reused if rows are deleted). This will make your INSERT INTO statement a lot easier.
In the following example, the revision column will be auto-incremented (unless the INSERT INTO statement explicitly provides a value for it, of course):
CREATE TABLE test (
id INTEGER PRIMARY KEY NOT NULL,
revision INTEGER,
description TEXT NOT NULL
);
CREATE TRIGGER auto_increment_trigger
AFTER INSERT ON test
WHEN new.revision IS NULL
BEGIN
UPDATE test
SET revision = (SELECT IFNULL(MAX(revision), 0) + 1 FROM test)
WHERE id = new.id;
END;
Now you can simply insert a new row like this, and the revision column will be auto-incremented:
INSERT INTO test (description) VALUES ('some description');

Related

How to Insert new Record into Table if the Record is not Present in the Table in Teradata

I want to insert a new record if the record is not present in the table
For that I am using below query in Teradata
INSERT INTO sample(id, name) VALUES('12','rao')
WHERE NOT EXISTS (SELECT id FROM sample WHERE id = '12');
When I execute the above query I am getting below error.
WHERE NOT EXISTS
Failure 3706 Syntax error: expected something between ')' and the 'WHERE' keyword.
Can anyone help with the above issue. It will be very helpful.
You can use INSERT INTO ... SELECT ... as follows:
INSERT INTO sample(id,name)
select '12','rao'
WHERE NOT EXISTS (SELECT id FROM sample WHERE id = '12');
You can also create the primary/unique key on id column to avoid inserting duplicate data in id column.
I would advise writing the query as:
INSERT INTO sample (id, name)
SELECT id, name
FROM (SELECT 12 as id, 'rao' as name) x
WHERE NOT EXISTS (SELECT 1 FROM sample s WHERE s.id = x.id);
This means that you do not need to repeat the constant value -- such repetition can be a cause of errors in queries. Note that I removed the single quotes. id looks like a number so treat it as a number.
The uniqueness of ids is usually handled using a unique constraint or index:
alter table sample add constraint unq_sample_id unique (id);
This makes sure that the database ensures uniqueness. Your approach can fail if two inserts are run at the same time with the same id. An attempt to insert a duplicates returns an error (which the exists can then avoid).
In practice, id columns are usually generated automatically by the database. So the create table statement would look more like:
id integer generated by default as identity
And the insert would look like:
insert into sample (name)
values (name);
If id is the Primary Index of the table you can use MERGE:
merge into sample as tgt
using VALUES('12','rao') as src (id, name)
on src.id = tgt.id
when not matched
then insert (src.id,src.name)

INSERT, and get the auto-incremented value

Consider the following table:
create table language (
id integer generated always as identity (START WITH 1, INCREMENT BY 1),
name long varchar,
constraint language_pk primary key (id)
);
To which I'd insert an entry this way.
insert into language(name) values ('value');
How does one know what value for id was created? Just doing a SELECT using the name field is not valid, because there can be duplicate entries.
Through plain SQL:
insert into language(name) values ('value');
SELECT IDENTITY_VAL_LOCAL();
See the manual for details: http://db.apache.org/derby/docs/10.7/ref/rrefidentityvallocal.html
When doing this from a Java class (through JDBC) you can use getGeneratedKeys() after "requesting" them with the approriate executeUpdate() method.
You use the JDBC method
st.execute(sql, Statement.RETURN_GENERATED_KEYS);
ResultSet keys = st.getGeneratedKeys();
as documented in the Derby manual.
See also Javadocs: DatabaseMetaData#supportsGetGeneratedKeys()
and Statement#getGeneratedKeys()
You could execute this statement (NB, not 100% sure this syntax is correct for Derby:
SELECT TOP 1 id FROM language ORDER BY id DESC
To find the last inserted ID.
Alternative for Derby:
SELECT MAX(id) from language
Obviously this will only be accurate if no other inserts (including inserts by other users) have happened between your insert and select.
See also this discussion:

Query regarding SQL Insert in SQL Server?

I am using SQL Server 2008 and developing a project which is in maintenance phase.
I want to insert record in a table whose primary key is an Integer but not an identity. e.g. table name is tblFiles and fields are ID, FileName, FileContent.
Actually that table is in use so I don’t want to make any schema change in it. And I want the key after row insertion because I have to put that in another table. Existing values in the Id column are different integer, means not in sequence.
So I want the query that also returns me the Id value. So I want to insert only FileName and FileContent and some sort of sql to whom I can embed in my insert query which insert a unique Id and also send me that id
Well, if it's not an IDENTITY field - don't you already have to specify the "ID" in your insert for it to succeed ? If so - you already have the ID! Or what am I missing? Is the ID determined by a trigger or something??
If so, try this query:
INSERT INTO dbo.tblFiles(FileName, FileContent)
OUTPUT inserted.ID
VALUES ('yourfile.name', 'your contents')
This should return the newly inserted ID from the INSERT query.
Marc
Change the Columns Identity Specification > Is Identity to Yes.
The after inserting into the table you can
Select SCOPE_IDENTITY()
to get the integer that was just added and return this in your SP.
If you really can't edit the database schema maybe you could add another table to the database that has two columns called ID and CurrentDate. Make the ID column an Identity. In your code insert into this table first select SCOPE_IDENTITY() and then use the integer returned to insert as the ID in your tblFles table.
P.S. Stop prefixing your table with tbl that's so 1999. :)
You could create a unique integer, not so elegantly, using
SELECT MAX(ID) + 1 FROM tblFiles
And simply return this from your query or sproc as the case maybe. Otherwise follow as marc_s says if it is known already.
UPDATE: have to say, rather than this fudge as requested, I would strongly recommend pushing back hard and getting table changed so this is an identity column, as this is what is. all answers so far are simply fudges, mine especially.
so my final query look like...
Insert into dbo.tblData (Id, FName, LName)
output inserted.Id
values ((SELECT MAX(ID) + 1 FROM dbo.tblData), 'xyz', 'abc')
We can assign max(ID)+1 in to an integer variable, then we can Insert
Declare #ID int
Select #ID = ISNULL(MAX(ID),0) + 1 FROM tblFiles
INSERT INTO tblFiles
(
ID, FileName, FileContent
)
Select #ID,'FileName','FileContent'
This insertion is direct,
INSERT INTO tblFiles
(
ID, FileName, FileContent
)
Select (Select ISNULL(MAX(ID),0) + 1 FROM tblFiles),'FileName','FileContent'
Here we have to use ISNULL condition because there is no data in table then it will return Null. So ISNULL(MAX(ID),0) + 1 this condition will give Data is null then 0+1=1.
Thank you

SQLite - UPSERT *not* INSERT or REPLACE

http://en.wikipedia.org/wiki/Upsert
Insert Update stored proc on SQL Server
Is there some clever way to do this in SQLite that I have not thought of?
Basically I want to update three out of four columns if the record exists,
If it does not exists I want to INSERT the record with the default (NUL) value for the fourth column.
The ID is a primary key so there will only ever be one record to UPSERT.
(I am trying to avoid the overhead of SELECT in order to determine if I need to UPDATE or INSERT obviously)
Suggestions?
I cannot confirm that Syntax on the SQLite site for TABLE CREATE.
I have not built a demo to test it, but it doesn't seem to be supported.
If it was, I have three columns so it would actually look like:
CREATE TABLE table1(
id INTEGER PRIMARY KEY ON CONFLICT REPLACE,
Blob1 BLOB ON CONFLICT REPLACE,
Blob2 BLOB ON CONFLICT REPLACE,
Blob3 BLOB
);
but the first two blobs will not cause a conflict, only the ID would
So I assume Blob1 and Blob2 would not be replaced (as desired)
UPDATEs in SQLite when binding data are a complete transaction, meaning
Each sent row to be updated requires: Prepare/Bind/Step/Finalize statements
unlike the INSERT which allows the use of the reset function
The life of a statement object goes something like this:
Create the object using sqlite3_prepare_v2()
Bind values to host parameters using sqlite3_bind_ interfaces.
Run the SQL by calling sqlite3_step()
Reset the statement using sqlite3_reset() then go back to step 2 and repeat.
Destroy the statement object using sqlite3_finalize().
UPDATE I am guessing is slow compared to INSERT, but how does it compare to SELECT using the Primary key?
Perhaps I should use the select to read the 4th column (Blob3) and then use REPLACE to write a new record blending the original 4th Column with the new data for the first 3 columns?
Assuming three columns in the table: ID, NAME, ROLE
BAD: This will insert or replace all columns with new values for ID=1:
INSERT OR REPLACE INTO Employee (id, name, role)
VALUES (1, 'John Foo', 'CEO');
BAD: This will insert or replace 2 of the columns... the NAME column will be set to NULL or the default value:
INSERT OR REPLACE INTO Employee (id, role)
VALUES (1, 'code monkey');
GOOD: Use SQLite On conflict clause
UPSERT support in SQLite! UPSERT syntax was added to SQLite with version 3.24.0!
UPSERT is a special syntax addition to INSERT that causes the INSERT to behave as an UPDATE or a no-op if the INSERT would violate a uniqueness constraint. UPSERT is not standard SQL. UPSERT in SQLite follows the syntax established by PostgreSQL.
GOOD but tedious: This will update 2 of the columns.
When ID=1 exists, the NAME will be unaffected.
When ID=1 does not exist, the name will be the default (NULL).
INSERT OR REPLACE INTO Employee (id, role, name)
VALUES ( 1,
'code monkey',
(SELECT name FROM Employee WHERE id = 1)
);
This will update 2 of the columns.
When ID=1 exists, the ROLE will be unaffected.
When ID=1 does not exist, the role will be set to 'Benchwarmer' instead of the default value.
INSERT OR REPLACE INTO Employee (id, name, role)
VALUES ( 1,
'Susan Bar',
COALESCE((SELECT role FROM Employee WHERE id = 1), 'Benchwarmer')
);
INSERT OR REPLACE is NOT equivalent to "UPSERT".
Say I have the table Employee with the fields id, name, and role:
INSERT OR REPLACE INTO Employee ("id", "name", "role") VALUES (1, "John Foo", "CEO")
INSERT OR REPLACE INTO Employee ("id", "role") VALUES (1, "code monkey")
Boom, you've lost the name of the employee number 1. SQLite has replaced it with a default value.
The expected output of an UPSERT would be to change the role and to keep the name.
Eric B’s answer is OK if you want to preserve just one or maybe two columns from the existing row. If you want to preserve a lot of columns, it gets too cumbersome fast.
Here’s an approach that will scale well to any amount of columns on either side. To illustrate it I will assume the following schema:
CREATE TABLE page (
id INTEGER PRIMARY KEY,
name TEXT UNIQUE,
title TEXT,
content TEXT,
author INTEGER NOT NULL REFERENCES user (id),
ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
Note in particular that name is the natural key of the row – id is used only for foreign keys, so the point is for SQLite to pick the ID value itself when inserting a new row. But when updating an existing row based on its name, I want it to continue to have the old ID value (obviously!).
I achieve a true UPSERT with the following construct:
WITH new (name, title, author) AS ( VALUES('about', 'About this site', 42) )
INSERT OR REPLACE INTO page (id, name, title, content, author)
SELECT old.id, new.name, new.title, old.content, new.author
FROM new LEFT JOIN page AS old ON new.name = old.name;
The exact form of this query can vary a bit. The key is the use of INSERT SELECT with a left outer join, to join an existing row to the new values.
Here, if a row did not previously exist, old.id will be NULL and SQLite will then assign an ID automatically, but if there already was such a row, old.id will have an actual value and this will be reused. Which is exactly what I wanted.
In fact this is very flexible. Note how the ts column is completely missing on all sides – because it has a DEFAULT value, SQLite will just do the right thing in any case, so I don’t have to take care of it myself.
You can also include a column on both the new and old sides and then use e.g. COALESCE(new.content, old.content) in the outer SELECT to say “insert the new content if there was any, otherwise keep the old content” – e.g. if you are using a fixed query and are binding the new values with placeholders.
This answer has been updated and so the comments below no longer apply.
2018-05-18 STOP PRESS.
UPSERT support in SQLite! UPSERT syntax was added to SQLite with version 3.24.0 (pending) !
UPSERT is a special syntax addition to INSERT that causes the INSERT to behave as an UPDATE or a no-op if the INSERT would violate a uniqueness constraint. UPSERT is not standard SQL. UPSERT in SQLite follows the syntax established by PostgreSQL.
alternatively:
Another completely different way of doing this: in my application I set my in memory rowID to be long.MaxValue when I create the row in memory. (MaxValue will never be used as an ID you won't live long enough....) Then if rowID is not that value then it must already be in the database so needs an UPDATE if it is MaxValue then it needs an insert. This is only useful if you can track the rowIDs in your app.
If you are generally doing updates I would ..
Begin a transaction
Do the update
Check the rowcount
If it is 0 do the insert
Commit
If you are generally doing inserts I would
Begin a transaction
Try an insert
Check for primary key violation error
if we got an error do the update
Commit
This way you avoid the select and you are transactionally sound on Sqlite.
I realize this is an old thread but I've been working in sqlite3 as of late and came up with this method which better suited my needs of dynamically generating parameterized queries:
insert or ignore into <table>(<primaryKey>, <column1>, <column2>, ...) values(<primaryKeyValue>, <value1>, <value2>, ...);
update <table> set <column1>=<value1>, <column2>=<value2>, ... where changes()=0 and <primaryKey>=<primaryKeyValue>;
It's still 2 queries with a where clause on the update but seems to do the trick. I also have this vision in my head that sqlite can optimize away the update statement entirely if the call to changes() is greater than zero. Whether or not it actually does that is beyond my knowledge, but a man can dream can't he? ;)
For bonus points you can append this line which returns you the id of the row whether it be a newly inserted row or an existing row.
select case changes() WHEN 0 THEN last_insert_rowid() else <primaryKeyValue> end;
Beginning with version 3.24.0 UPSERT is supported by SQLite.
From the documentation:
UPSERT is a special syntax addition to INSERT that causes the INSERT to behave as an UPDATE or a no-op if the INSERT would violate a uniqueness constraint. UPSERT is not standard SQL. UPSERT in SQLite follows the syntax established by PostgreSQL. UPSERT syntax was added to SQLite with version 3.24.0 (pending).
An UPSERT is an ordinary INSERT statement that is followed by the special ON CONFLICT clause
Image source: https://www.sqlite.org/images/syntax/upsert-clause.gif
Example:
CREATE TABLE t1(id INT PRIMARY KEY, c TEXT);
INSERT INTO t1(id, c) VALUES (1,'a'), (2, 'b');
SELECT * FROM t1;
INSERT INTO t1(id, c) VALUES (1, 'c');
-- UNIQUE constraint failed: t1.id
INSERT INTO t1(id, c) VALUES (1, 'c')
ON CONFLICT DO NOTHING;
SELECT * FROM t1;
INSERT INTO t1(id, c)
VALUES (1, 'c')
ON CONFLICT(id) DO UPDATE SET c = excluded.c;
SELECT * FROM t1;
db<>fiddle demo
Here is a solution that really is an UPSERT (UPDATE or INSERT) instead of an INSERT OR REPLACE (which works differently in many situations).
It works like this:
1. Try to update if a record with the same Id exists.
2. If the update did not change any rows (NOT EXISTS(SELECT changes() AS change FROM Contact WHERE change <> 0)), then insert the record.
So either an existing record was updated or an insert will be performed.
The important detail is to use the changes() SQL function to check if the update statement hit any existing records and only perform the insert statement if it did not hit any record.
One thing to mention is that the changes() function does not return changes performed by lower-level triggers (see http://sqlite.org/lang_corefunc.html#changes), so be sure to take that into account.
Here is the SQL...
Test update:
--Create sample table and records (and drop the table if it already exists)
DROP TABLE IF EXISTS Contact;
CREATE TABLE [Contact] (
[Id] INTEGER PRIMARY KEY,
[Name] TEXT
);
INSERT INTO Contact (Id, Name) VALUES (1, 'Mike');
INSERT INTO Contact (Id, Name) VALUES (2, 'John');
-- Try to update an existing record
UPDATE Contact
SET Name = 'Bob'
WHERE Id = 2;
-- If no record was changed by the update (meaning no record with the same Id existed), insert the record
INSERT INTO Contact (Id, Name)
SELECT 2, 'Bob'
WHERE NOT EXISTS(SELECT changes() AS change FROM Contact WHERE change <> 0);
--See the result
SELECT * FROM Contact;
Test insert:
--Create sample table and records (and drop the table if it already exists)
DROP TABLE IF EXISTS Contact;
CREATE TABLE [Contact] (
[Id] INTEGER PRIMARY KEY,
[Name] TEXT
);
INSERT INTO Contact (Id, Name) VALUES (1, 'Mike');
INSERT INTO Contact (Id, Name) VALUES (2, 'John');
-- Try to update an existing record
UPDATE Contact
SET Name = 'Bob'
WHERE Id = 3;
-- If no record was changed by the update (meaning no record with the same Id existed), insert the record
INSERT INTO Contact (Id, Name)
SELECT 3, 'Bob'
WHERE NOT EXISTS(SELECT changes() AS change FROM Contact WHERE change <> 0);
--See the result
SELECT * FROM Contact;
Updates from Bernhardt:
You can indeed do an upsert in SQLite, it just looks a little different than you are used to. It would look something like:
INSERT INTO table_name (id, column1, column2)
VALUES ("youruuid", "value12", "value2")
ON CONFLICT(id) DO UPDATE
SET column1 = "value1", column2 = "value2"
The best approach I know is to do an update, followed by an insert.
The "overhead of a select" is necessary, but it is not a terrible burden since you are searching on the primary key, which is fast.
You should be able to modify the below statements with your table & field names to do what you want.
--first, update any matches
UPDATE DESTINATION_TABLE DT
SET
MY_FIELD1 = (
SELECT MY_FIELD1
FROM SOURCE_TABLE ST
WHERE ST.PRIMARY_KEY = DT.PRIMARY_KEY
)
,MY_FIELD2 = (
SELECT MY_FIELD2
FROM SOURCE_TABLE ST
WHERE ST.PRIMARY_KEY = DT.PRIMARY_KEY
)
WHERE EXISTS(
SELECT ST2.PRIMARY_KEY
FROM
SOURCE_TABLE ST2
,DESTINATION_TABLE DT2
WHERE ST2.PRIMARY_KEY = DT2.PRIMARY_KEY
);
--second, insert any non-matches
INSERT INTO DESTINATION_TABLE(
MY_FIELD1
,MY_FIELD2
)
SELECT
ST.MY_FIELD1
,NULL AS MY_FIELD2 --insert NULL into this field
FROM
SOURCE_TABLE ST
WHERE NOT EXISTS(
SELECT DT2.PRIMARY_KEY
FROM DESTINATION_TABLE DT2
WHERE DT2.PRIMARY_KEY = ST.PRIMARY_KEY
);
Expanding on Aristotle’s answer you can SELECT from a dummy 'singleton' table (a table of your own creation with a single row). This avoids some duplication.
I've also kept the example portable across MySQL and SQLite and used a 'date_added' column as an example of how you could set a column only the first time.
REPLACE INTO page (
id,
name,
title,
content,
author,
date_added)
SELECT
old.id,
"about",
"About this site",
old.content,
42,
IFNULL(old.date_added,"21/05/2013")
FROM singleton
LEFT JOIN page AS old ON old.name = "about";
If someone wants to read my solution for SQLite in Cordova, I got this generic js method thanks to #david answer above.
function addOrUpdateRecords(tableName, values, callback) {
get_columnNames(tableName, function (data) {
var columnNames = data;
myDb.transaction(function (transaction) {
var query_update = "";
var query_insert = "";
var update_string = "UPDATE " + tableName + " SET ";
var insert_string = "INSERT INTO " + tableName + " SELECT ";
myDb.transaction(function (transaction) {
// Data from the array [[data1, ... datan],[()],[()]...]:
$.each(values, function (index1, value1) {
var sel_str = "";
var upd_str = "";
var remoteid = "";
$.each(value1, function (index2, value2) {
if (index2 == 0) remoteid = value2;
upd_str = upd_str + columnNames[index2] + "='" + value2 + "', ";
sel_str = sel_str + "'" + value2 + "', ";
});
sel_str = sel_str.substr(0, sel_str.length - 2);
sel_str = sel_str + " WHERE NOT EXISTS(SELECT changes() AS change FROM "+tableName+" WHERE change <> 0);";
upd_str = upd_str.substr(0, upd_str.length - 2);
upd_str = upd_str + " WHERE remoteid = '" + remoteid + "';";
query_update = update_string + upd_str;
query_insert = insert_string + sel_str;
// Start transaction:
transaction.executeSql(query_update);
transaction.executeSql(query_insert);
});
}, function (error) {
callback("Error: " + error);
}, function () {
callback("Success");
});
});
});
}
So, first pick up the column names with this function:
function get_columnNames(tableName, callback) {
myDb.transaction(function (transaction) {
var query_exec = "SELECT name, sql FROM sqlite_master WHERE type='table' AND name ='" + tableName + "'";
transaction.executeSql(query_exec, [], function (tx, results) {
var columnParts = results.rows.item(0).sql.replace(/^[^\(]+\(([^\)]+)\)/g, '$1').split(','); ///// RegEx
var columnNames = [];
for (i in columnParts) {
if (typeof columnParts[i] === 'string')
columnNames.push(columnParts[i].split(" ")[0]);
};
callback(columnNames);
});
});
}
Then build the transactions programmatically.
"Values" is an array you should build before and it represents the rows you want to insert or update into the table.
"remoteid" is the id I used as a reference, since I'm syncing with my remote server.
For the use of the SQLite Cordova plugin, please refer to the official link
I think this may be what you are looking for: ON CONFLICT clause.
If you define your table like this:
CREATE TABLE table1(
id INTEGER PRIMARY KEY ON CONFLICT REPLACE,
field1 TEXT
);
Now, if you do an INSERT with an id that already exists, SQLite automagically does UPDATE instead of INSERT.
Hth...
This method remixes a few of the other methods from answer in for this question and incorporates the use of CTE (Common Table Expressions). I will introduce the query then explain why I did what I did.
I would like to change the last name for employee 300 to DAVIS if there is an employee 300. Otherwise, I will add a new employee.
Table Name: employees
Columns: id, first_name, last_name
The query is:
INSERT OR REPLACE INTO employees (employee_id, first_name, last_name)
WITH registered_employees AS ( --CTE for checking if the row exists or not
SELECT --this is needed to ensure that the null row comes second
*
FROM (
SELECT --an existing row
*
FROM
employees
WHERE
employee_id = '300'
UNION
SELECT --a dummy row if the original cannot be found
NULL AS employee_id,
NULL AS first_name,
NULL AS last_name
)
ORDER BY
employee_id IS NULL --we want nulls to be last
LIMIT 1 --we only want one row from this statement
)
SELECT --this is where you provide defaults for what you would like to insert
registered_employees.employee_id, --if this is null the SQLite default will be used
COALESCE(registered_employees.first_name, 'SALLY'),
'DAVIS'
FROM
registered_employees
;
Basically, I used the CTE to reduce the number of times the select statement has to be used to determine default values. Since this is a CTE, we just select the columns we want from the table and the INSERT statement uses this.
Now you can decide what defaults you want to use by replacing the nulls, in the COALESCE function with what the values should be.
Following Aristotle Pagaltzis and the idea of COALESCE from Eric B’s answer, here it is an upsert option to update only few columns or insert full row if it does not exist.
In this case, imagine that title and content should be updated, keeping the other old values when existing and inserting supplied ones when name not found:
NOTE id is forced to be NULL when INSERT as it is supposed to be autoincrement. If it is just a generated primary key then COALESCE can also be used (see Aristotle Pagaltzis comment).
WITH new (id, name, title, content, author)
AS ( VALUES(100, 'about', 'About this site', 'Whatever new content here', 42) )
INSERT OR REPLACE INTO page (id, name, title, content, author)
SELECT
old.id, COALESCE(old.name, new.name),
new.title, new.content,
COALESCE(old.author, new.author)
FROM new LEFT JOIN page AS old ON new.name = old.name;
So the general rule would be, if you want to keep old values, use COALESCE, when you want to update values, use new.fieldname
If you don't mind doing this in two operations.
Steps:
1) Add new items with "INSERT OR IGNORE"
2) Update existing items with "UPDATE"
The input to both steps is the same collection of new or update-able items. Works fine with existing items that need no changes. They will be updated, but with the same data and therefore net result is no changes.
Sure, slower, etc. Inefficient. Yep.
Easy to write the sql and maintain and understand it? Definitely.
It's a trade-off to consider.
Works great for small upserts. Works great for those that don't mind sacrificing efficiency for code maintainability.
Complete example of upserting using WHERE to select the newer dated record.
-- https://www.db-fiddle.com/f/7jyj4n76MZHLLk2yszB6XD/22
DROP TABLE IF EXISTS db;
CREATE TABLE db
(
id PRIMARY KEY,
updated_at,
other
);
-- initial INSERT
INSERT INTO db (id,updated_at,other) VALUES(1,1,1);
SELECT * FROM db;
-- INSERT without WHERE
INSERT INTO db (id,updated_at,other) VALUES(1,2,2)
ON CONFLICT(id) DO UPDATE SET updated_at=excluded.updated_at;
SELECT * FROM db;
-- WHERE is FALSE
INSERT INTO db (id,updated_at,other) VALUES(1,2,3)
ON CONFLICT(id) DO UPDATE SET updated_at=excluded.updated_at, other=excluded.other
WHERE excluded.updated_at > updated_at;
SELECT * FROM db;
-- ok to SET a PRIMARY KEY. WHERE is TRUE
INSERT INTO db (id,updated_at,other) VALUES(1,3,4)
ON CONFLICT(id) DO UPDATE SET id=excluded.id, updated_at=excluded.updated_at, other=excluded.other
WHERE excluded.updated_at > updated_at;
SELECT * FROM db;
Having just read this thread and been disappointed that it wasn't easy to just to this "UPSERT"ing, I investigated further...
You can actually do this directly and easily in SQLITE.
Instead of using: INSERT INTO
Use: INSERT OR REPLACE INTO
This does exactly what you want it to do!
SELECT COUNT(*) FROM table1 WHERE id = 1;
if COUNT(*) = 0
INSERT INTO table1(col1, col2, cole) VALUES(var1,var2,var3);
else if COUNT(*) > 0
UPDATE table1 SET col1 = var4, col2 = var5, col3 = var6 WHERE id = 1;

iSeries DB2 - Is there any way to select the identity value from an insert statement?

I know we're rare, us poor folk that are using iSeries for DB2/AS400, but I'm hoping someone can answer this simple question. Is there any way to return the identity value from an insert statement without using two lines of SQL? I'm being forced to use inline SQL in C# to perform an insert, and then I need to use the identity generated for the insert for something later on. Simply put, I need the iSeries DB2 equivalent of Oracle's "RETURNING." I.e.,
INSERT INTO AwesomeTable (column1, column2, etc.)
VALUES (value1, value2, etc.)
RETURNING something;
Anyone? Thanks in advance.
EDIT: Unless someone knows of a way I can execute two lines of SQL in one IBM.Data.DB2.iSeries.iDB2Command (not a stored proc), I would like to do this all in one line of SQL
I am not sure of iSeries, but the following worked on db2v8.1:
Consider 'ID' is the name of your identity column. The following stmt will return the newly generated id (the same one that gets inserted by the insert stmt):
SELECT ID FROM FINAL TABLE (
INSERT INTO AwesomeTable (column1, column2, etc.)
VALUES (value1, value2, etc.)
)
Some explanation I found on the publib site: (I used it for reference to test my query above)
/* The following SELECT statement references an INSERT statement in its
FROM clause. It inserts an employee record from host variables into
table company_b. The current employee ID from the cursor is selected
into the host variable new_id. The keywords FROM FINAL TABLE
determine that the value in new_id is the value of ID after the
INSERT statement is complete.
Note that the ID column in table company_b is generated and without
the SELECT statement an additional query would have to be made in
order to retreive the employee's ID number.
*/
EXEC SQL SELECT ID INTO :new_id
FROM FINAL TABLE(INSERT INTO company_b
VALUES(default, :name, :department, :job, :years, :salary,
:benefits, :id));
Hope this helps :)
You need to use the IDENTITY_VAL_LOCAL scalar function. From the IBM documentation:
IDENTITY_VAL_LOCAL is a
non-deterministic function that
returns the most recently assigned
value for an identity column.
Example:
CREATE TABLE EMPLOYEE
(EMPNO INTEGER GENERATED ALWAYS AS IDENTITY,
NAME CHAR(30),
SALARY DECIMAL(5,2),
DEPT SMALLINT)
INSERT INTO EMPLOYEE
(NAME, SALARY, DEPTNO)
VALUES('Rupert', 989.99, 50)
SELECT IDENTITY_VAL_LOCAL() FROM SYSIBM.SYSDUMMY1
Here's an example :
CREATE TABLE AUTOINC (
AUTO91 INTEGER GENERATED ALWAYS AS IDENTITY,
SCDS91 CHAR(35) NOT NULL DEFAULT '',
MCLD91 DECIMAL(3,0) NOT NULL DEFAULT 0,
CONSTRAINT PK_AUTOINC PRIMARY KEY(AUTO91));
// Notice the default keyword where the auto increment field is.
insert into AUTOINC Values( default ,'SYSC' , 0 )
// And using the function to return the last identity column value.
// Note: fetch first row only.
select **IDENTITY_VAL_LOCAL**() from AUTOINC **fetch first row only**