How to get the serial id just after inserting a row? [duplicate] - java

This question already has answers here:
How to get a value from the last inserted row? [duplicate]
(14 answers)
Closed 9 years ago.
I have a table with row 'id' (a primary key) default set to serial in PostgreSQL. I insert into this row by calling
session.getCurrentSession().createSQLQuery("some insert query")
without adding any value into id as it is default set to serial.
How can I retrieve the `id' of just inserted row?

JDBC statements can return the generated keys. For instance, if the table has a single column id of type serial (probably PK) that is not mentioned in the insert SQL below, the generated value for this column can be obtained as:
PreparedStatement s = connection.createStatement
("INSERT INTO my_table (c,d) VALUES (1,2)",
Statement.RETURN_GENERATED_KEYS);
s.executeUpdate();
ResultSet keys = s.getGeneratedKeys();
int id = keys.getInt(1);
This is faster than sending the second query to obtain the sequence value or max column value later. Also depending on circumstances these two other solutions may not be not be thread safe.

Since it is serial you can use select max(id) from tableName
Using max(id) is a very bad idea. It will not give you the correct result
in case of multiple concurrent transactions. The only correct way is to use
curval() or the returning clause.
In posgresql: There is already a stackoverflow-question exists BTW.
`INSERT INTO tableName(id, name) VALUES(DEFAULT, 'bob') RETURNING id;`
(also)
Get a specific sequence:
SELECT currval('name_of_your_sequence');
Get the last value from the last sequence used:
SELECT lastval();
Manual: http://www.postgresql.org/docs/current/static/functions-sequence.html
For PHP-mysql users:
From php.net clickhere
<?php
$link = mysqli_connect('localhost', 'mysql_user', 'mysql_password');
if (!$link) {
die('Could not connect: ' . mysqli::$connect_error() );
}
mysqli::select_db('mydb');
mysqli::query("INSERT INTO mytable (product) values ('kossu')");
printf("Last inserted record has id %d\n", mysqli::$insert_id());
?>
But you need to connect for every query.

use SELECT CURRVAL(); . Typically used in conjunction with pg_get_serial_sequence
postgreSQL function for last inserted ID

Related

How can I access a value when inserting into a table?

I'm trying to write a java sql query, the simplified table would be table(name,version) with a unique constraint on (name, version).
I'm trying to insert a row into my database with a conditional statement. Meaning that when a entry with the same name exists, it should insert the row with same name and its version increased by 1.
I have tried with the following:
INSERT INTO table(name,version)
VALUES(?, CASE WHEN EXISTS(SELECT name from table where name=?)
THEN (SELECT MAX(version) FROM table WHERE name = ?) +1
ELSE 1 END)
values are sent by user.
My question is, how can I access the 'name' inside the values so I could compare them?
If you want to write this as a single query:
INSERT INTO table (name, version)
SELECT ?, COLAESCE(MAX(t2.version) + 1, 1)
FROM table t2
WHERE t2.name = ?;
That said, this is dangerous. Two threads could execute this query "at the same time" and possibly create the same version number. You can prevent this from happening by adding a unique index/constraint on (name, version).
With the unique index/constraint, one of the updates will fail if there is a conflict.
I see at least two approaches:
1. For each pair of name and version you first query the max version:
SELECT MAX(VERSION) as MAX FROM <table> WHERE NAME = <name>
And then you insert the result + 1 with a corresponding insert query:
INSERT INTO <table>(NAME,VERSION) VALUES (<name>,result+1)
This approach is very straight-forward, easy-to-read and implement, however, not really performant because of so many queries necessary.
You can achieve that with sql alone with sql analytics and window functions, e.g.:
SELECT NAME, ROW_NUMBER() over (partition BY NAME ORDER BY NAME) as VERSION FROM<table>
You can then save the result of this query as a table using CREATE TABLE as SELECT...
(The assumption here is that the first version is 1, if it is not the case, then one could slightly rework the query). This solution would be very performant even for large datasets.
You should get the name before insertion. In your case, if something went wrong then how would you know about it so you get the name before insert query.
Not sure but you try this:
declare int version;
if exists(SELECT name from table where name=?)
then
version = SELECT MAX(version) FROM table WHERE name = ?
version += 1
else
version = 1
end
Regards.
This is actually a bad plan, you might be changing what the user's specified data. That is likely to not be what is desired, maybe they're not trying to create a new version but just unaware that the one wanted already exists. But, you can create a function, which your java calls, not only inserts the requested version or max+1 if the requested version already exists. Moreover it returns the actual values inserted.
-- create table
create table nv( name text
, version integer
, constraint nv_uk unique (name, version)
);
-- function to create version or 1+max if requested exists
create or replace function new_version
( name_in text
, version_in integer
)
returns record
language plpgsql strict
as $$
declare
violated_constraint text;
return_name_version record;
begin
insert into nv(name,version)
values (name_in,version_in)
returning (name, version) into return_name_version;
return return_name_version;
exception
when unique_violation
then
GET STACKED DIAGNOSTICS violated_constraint = CONSTRAINT_NAME;
if violated_constraint like '%nv\_uk%'
then
insert into nv(name,version)
select name_in, 1+max(version)
from nv
where name = name_in
group by name_in
returning (name, version) into return_name_version;
return return_name_version;
end if;
end;
$$;
-- create some data
insert into nv(name,version)
select 'n1', gn
from generate_series( 1,3) gn ;
-- test insert existing
select new_version('n2',1);
select new_version('n1',1);
select *
from nv
order by name, version;

JDBC: Generating the value of primary key for prepared statements [duplicate]

This question already has answers here:
Is there a way to retrieve the autoincrement ID from a prepared statement
(5 answers)
Illegal operation on empty result set [duplicate]
(2 answers)
Closed 7 years ago.
Suppose I want to add a new row to my table via JDBC. In my table, I have an auto incrementing primary key field (so I can update the table later), and another regular field.
userid BIGINT AUTO_INCREMENT PRIMARY KEY,
username TEXT,
Now, I am creating the new statement and executing it using prepared statements, like so:
//dummy execute to get the generated keys
stmt.execute("SELECT * FROM user;", Statement.RETURN_GENERATED_KEYS);
ResultSet rs = stmt.getGeneratedKeys();
int id=1;
//this is never executed, the resultset is always empty...
if(rs.next())
{
System.out.println("not empty");
id = rs.getInt(1);
}
System.out.println(id); //therefore, id is always 1
//prepare a statement to execute in SQL
stmt=con.prepareStatement("INSERT INTO user VALUES (?,?);", Statement.RETURN_GENERATED_KEYS);
//fill in the ?'s with their respective values
((PreparedStatement) stmt).setString(1, String.valueOf(id));
((PreparedStatement) stmt).setString(2, user);
//execute statement
((PreparedStatement) stmt).executeUpdate();
As you see, I want the value of the generated key so that I can use a prepared statement to update all the columns in the newly generated row (otherwise I get a No value specified for parameter 1 error).
But when I do the above code, I get an
Duplicate entry '1' for key 'PRIMARY'
This seems to me that the resultset is always empty. So I am not accessing the value correctly. Why is this so, and how can I fix this so that I can use my same structure of prepared statements to execute these queries?
You can call getGeneratedKeys only after you have executed you statement, not before. See https://docs.oracle.com/javase/8/docs/api/java/sql/Statement.html#getGeneratedKeys--
Simply preparing the statement does not generate the new key. Just drop the id column from your insert and insert only user.
1.Since your id is Auto Increment Column you should pass the value for that filed for first time (try to do it through MYSQL server directly.
2.Don't try to perform operations on empty result set.
3.In your case id always will be 1 since if statement doesn't execute.
Thank you.

How can I get an id for a new record in a generic way? (JOOQ 3.4 with Postgres)

With jooq 3.4 I can't figure out how to do this (with Postgresql):
Query query = dsl.insertInto(TABLE)
.set(TABLE.ID, Sequences.TABLE_ID_SEQ.nextval());
but in a case when I don't know which is the exact table, something like this:
TableImpl<?> tableImpl;
Query query = dsl.insertInto(tableImpl)
.set(tableImpl.getIdentity(), tableImpl.getIdentity().getSequence().nextval());
Is it somehow possible?
I tried this:
dsl.insertInto(tableImpl)
.set(DSL.field("id"),
tableImpl.getSchema().getSequence("table_id_seq").nextval())
This works but I still don't know how to get the sequence name from the TableImpl object.
Is there a solution for this? Or is there a problem with my approach?
In plain SQL I would do this:
insert into table_A (id) VALUES nextval('table_A_id_seq');
insert into table_B (table_A_id, some_val) VALUES (currval('table_A_id_seq'), some_val);
So I need the value or a reference to that id for later use of the id that was generated for the inserted record as default, but I don't want to set any other values.
jOOQ currently doesn't have any means of associating a table with its implicitly used sequence for the identity column. The reason for this is that the sequence is generated when the table is created, but it isn't formally connected to that table.
Usually, you don't have to explicitly set the serial value of a column in a PostgreSQL database. It is generated automatically on insert. In terms of DDL, this means:
CREATE TABLE tablename (
colname SERIAL
);
is equivalent to specifying:
CREATE SEQUENCE tablename_colname_seq;
CREATE TABLE tablename (
colname integer NOT NULL DEFAULT nextval('tablename_colname_seq')
);
ALTER SEQUENCE tablename_colname_seq OWNED BY tablename.colname;
The above is taken from:
http://www.postgresql.org/docs/9.3/static/datatype-numeric.html#DATATYPE-SERIAL
In other words, just leave out the ID values from the INSERT statements.
"Empty" INSERT statements
Note that if you want to create an "empty" INSERT statement, i.e. a statement where you pass no values at all, generating a new column with a generated ID, you can use the DEFAULT VALUES clause.
With SQL
INSERT INTO tablename DEFAULT VALUES
With jOOQ
DSL.using(configuration)
.insertInto(TABLENAME)
.defaultValues()
.execute();
Returning IDs
Note that PostgreSQL has native support for an INSERT .. RETURNING clause, which is also supported by jOOQ:
With SQL
INSERT INTO tablename (...) VALUES (...) RETURNING ID
With jOOQ
DSL.using(configuration)
.insertInto(TABLENAME, ...)
.values(...)
.returning(TABLENAME.ID)
.fetchOne();

Java update when data exists and insert if doesnt [duplicate]

In MySQL, if you specify ON DUPLICATE KEY UPDATE and a row is inserted that would cause a duplicate value in a UNIQUE index or PRIMARY KEY, an UPDATE of the old row is performed. For example, if column a is declared as UNIQUE and contains the value 1, the following two statements have identical effect:
INSERT INTO table (a,b,c) VALUES (1,2,3)
ON DUPLICATE KEY UPDATE c=c+1;
UPDATE table SET c=c+1 WHERE a=1;
I don't believe I've come across anything of the like in T-SQL. Does SQL Server offer anything comparable to MySQL's ON DUPLICATE KEY UPDATE?
I was surprised that none of the answers on this page contained an example of an actual query, so here you go:
A more complex example of inserting data and then handling duplicate
MERGE
INTO MyBigDB.dbo.METER_DATA WITH (HOLDLOCK) AS target
USING (SELECT
77748 AS rtu_id
,'12B096876' AS meter_id
,56112 AS meter_reading
,'20150602 00:20:11' AS time_local) AS source
(rtu_id, meter_id, meter_reading, time_local)
ON (target.rtu_id = source.rtu_id
AND target.time_local = source.time_local)
WHEN MATCHED
THEN UPDATE
SET meter_id = '12B096876'
,meter_reading = 56112
WHEN NOT MATCHED
THEN INSERT (rtu_id, meter_id, meter_reading, time_local)
VALUES (77748, '12B096876', 56112, '20150602 00:20:11');
There's no DUPLICATE KEY UPDATE equivalent, but MERGE and WHEN MATCHED might work for you
Inserting, Updating, and Deleting Data by Using MERGE
You can try the other way around. It does the same thing more or less.
UPDATE tablename
SET field1 = 'Test1',
field2 = 'Test2'
WHERE id = 1
IF ##ROWCOUNT = 0
INSERT INTO tablename
(id,
field1,
field2)
VALUES (1,
'Test1',
'Test2')
SQL Server 2008 has this feature, as part of TSQL.
See documentation on MERGE statement here - http://msdn.microsoft.com/en-us/library/bb510625.aspx
SQL server 2000 onwards has a concept of instead of triggers, which can accomplish the wanted functionality - although there will be a nasty trigger hiding behind the scenes.
Check the section "Insert or update?"
http://msdn.microsoft.com/en-us/library/aa224818(SQL.80).aspx

Getting the last inserted record id of a database table in java? [duplicate]

This question already has answers here:
How to get the insert ID in JDBC?
(14 answers)
Closed 7 years ago.
I have a database table A which stores records, A has a primary key (recordid) with auto_increment, each time i insert a record in to A, i get the inserted recordid and store it in another masterTable.
I am using a select statement as soon i do an insert into A to get the recordid like this:
select recordid from A order by recordid DESC LIMIT 1;
But i ran into a problem today, where in two records were inserted(by different threads) at the same time and i ended up storing wrong recordid in the master id( the same recordid for both the txns)
I heard about Statement.getGeneratedKeys(), I would like to know if that really helps resolve the issue. Or what is the best way to handle this.
You can use the getGeneratedKeys method. This forum post will help.
May I also recommend that you use an ORM tool like Hibernate. In Hibernate you would do something like this:
myTable = new myTable();
myTable.prop1 = prop1;
myTable.prop2 = prop2;
int id = session.save(myTable);
Hibernate will issue the appropriate SQL commands (depending on the database selected) and return you the auto-generated id.
The MySQL JDBC driver does support the getGeneratedKey() method. Have a look at the section 20.3.5.1.4. Retrieving AUTO_INCREMENT Column Values of the MySQL manual where:
we demonstrates the use of the new JDBC-3.0 method getGeneratedKeys() which is now the preferred method to use if you need to retrieve AUTO_INCREMENT keys.
In databases that don't support generatedKeys you may be able to get the ID into a return parameter. Oracle for example provides the RETURNING xxx INTO ? syntax where xxx is your column name.

Categories