This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
rs.last() gives Invalid operation for forward only resultset : last
So I'm trying to understand the result set cursor and I'm having an issue with where the cursor is apparently.
I have a very small application that assigns a new integer id automatically, which will be the last entry into the database, therefore the highest integer. I'm trying to get to the last entry like this (rs is result set) so I can use its value:
rs.last
and then assigning the_new_id to rs.getInt(1)...
However, I get the "Invalid operation for forward only resultset : last" sql exception.
Right now I have a big "kludge" to make this work:
while(rs.next())
your_new_id = rs.getInt(1);
and then I just assign the new id that way. :-\
How can I implement this same behavior more elegantly using last?
Any help is appreciated.
By default, result sets are forward-only, meaning that the only thing you can do to change the position of the cursor is next() (which is all you need if you order by ID in descending order).
The javadoc explains it:
A default ResultSet object is not updatable and has a cursor that
moves forward only. Thus, you can iterate through it only once and
only from the first row to the last row. It is possible to produce
ResultSet objects that are scrollable and/or updatable. The following
code fragment, in which con is a valid Connection object, illustrates
how to make a result set that is scrollable and insensitive to updates
by others, and that is updatable. See ResultSet fields for other
options.
Statement stmt = con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_UPDATABLE);
ResultSet rs = stmt.executeQuery("SELECT a, b FROM TABLE2");
// rs will be scrollable, will not show changes made by others,
// and will be updatable
Apparently you are trying to retrieve the ID that was generated by a previous INSERT statement. You should not use a separate SELECT statement for that (which is not transaction safe and does impose an unnecessary load on the database).
To retrieve a generated ID, use the following JDBC calls:
String insert = "insert into some_table (... ";
PreparedStatement pstmt = con.prepareStatement(insert, new String[] {"ID"});
int rowsInserted = pstmt.executeUpdate();
ResultSet idResult = pstmt.getGeneratedKeys();
int newId = -1;
if (rs.next()) {
newId = rs.getINt(1);
}
This will retrieve the value that was generated for the ID column during the INSERT. This will be faster than doing a SELECT to get the latest ID, but more importantly it is transaction safe.
Related
I can't figure out if this is an acceptable operation. I need to select records from the SQL Server 2008 database and then delete them, all as a single transaction from an ASP.NET code. Note that the .NET code must be able to retrieve the data that was first selected.
Something as such:
SELECT * FROM [tbl] WHERE [id] > 6;
DELETE FROM [tbl] WHERE [id] > 6
I'm trying it with the SQL Fiddle but then if I do:
SELECT * FROM [tbl]
I get the full table as if nothing was deleted.
EDIT As requested below here's the full .NET code to retrieve the records:
string strSQLStatement = "SELECT * FROM [tbl] WHERE [id] > 6;" +
"DELETE FROM [tbl] WHERE [id] > 6";
using (SqlCommand cmd = new SqlCommand(strSQLStatement, connectionString))
{
using (SqlDataReader rdr = cmd.ExecuteReader())
{
while(rdr.Read())
{
//Read values
val0 = rdr.GetInt32(0);
val3 = rdr.GetInt32(3);
//etc.
}
}
}
This will do the select and delete simultanious:
delete from [tbl] output deleted.* WHERE [id] > 6
It is possible to select and delete in the same transaction as long as both operations are enlisted in the same transaction.
Look at this post
Transactions in .net
The "easiest" way to achieve transactions with a compatible provider (SQL Server works great!) is to use a TransactionScope. Just make sure the scope is created before the connection is opened so that everything is correctly enlisted.
The content of the SelectStuff and DeleteStuff methods doesn't matter much - just use the same connection, don't manually mess with the connection or with transactions, and perform the SQL operations however is best.
// Notes
// - Create scope OUTSIDE/BEFORE connection for automatic enlisting
// - Create only ONE connection inside to avoid DTC and "advanced behavior"
using (var ts = new TransactionScope())
using (var conn = CreateConnection()) {
// Make sure stuff selected is MATERIALIZED:
// If a LAZY type (Enumerable/Queryable) is returned and used later it
// may cause access to the connection outside of when it is valid!
// Use "ToList" as required to force materialization of such sequences.
var selectedStuff = SelectStuff(conn);
DeleteStuff(conn);
// Commit
ts.Complete();
// Know stuff is deleted here, and access selected stuff.
return selectedStuff;
}
The return value from multiple SQL statements is the result of the last statement run, which in this case is the DELETE. There are no rows returned from a DELETE, so there is nothing to read for val0 and val3.
There are two solutions I can think of here:
Change your code to expressly start a transaction, perform the SELECT, read the values, and then issue the DELETE, or
SELECT into a #temp table, execute the DELETE, and then SELECT from the #temp table, do what you need to with the rows, and then DROP th.
I obtained the following code example from http://docs.oracle.com/javase/1.4.2/docs/guide/jdbc/getstart/preparedstatement.html
I have three questions:
What does 'keyColumn' refer to considering that there are only three columns - LAST, FIRST, HOME.
Why is a loop used in iterating the generated keys? Are multiple rows returned for that one insert statement?
Which databases support multiple generated keys per table?
String sql = "INSERT INTO AUTHORS (LAST, FIRST, HOME) VALUES " +
"(?, ?, ?, keyColumn)";
PreparedStatement addAuthor = con.prepareStatement(sql,
Statement.RETURN_GENERATED_KEYS);
addAuthor.setString(1, "Wordsworth");
addAuthor.setString(2, "William");
addAuthor.setString(3, "England");
int rows = addAuthor.executeUpdate();
ResultSet rs = stmt.getGeneratedKeys();
if (rs.next()) {
ResultSetMetaData rsmd = rs.getMetaData();
int colCount = rsmd.getColumnCount();
do {
for (int i = 1; i <= colCount; i++) {
String key = rs.getString(i);
System.out.println("key " + i + "is " + key);
}
}
while (rs.next();)
}
else {
System.out.println("There are no generated keys.");
}
Question 1. I think that keyColumn in the query on your link is simply an error in the example. The second and third example in that paragraph also contain serious syntax errors. I wouldn't dwell on it. This documentation has been removed entirely from more recent Java versions.
Question 2. Think about statements like INSERT INTO ... SELECT ... FROM ... or INSERT INTO ... VALUES (...), (...) which can produce multiple inserted rows. Also some database support returning values from other DML (eg UPDATE or DELETE), which can also affect multiple rows, that is why you need to consider a loop. In this specific example it is not necessary as you can be sure only one row will be inserted.
Question 3. This question is a bit more complex:
Some databases (or drivers) can't easily decide what is the actual generated column. For example because the database doesn't have IDENTITY columns, but use triggers to generate keys. Identifying generated keys would involve parsing all triggers on a table to check if it assigns a generated value to a (primary key or other) column, which is not easily done and would be error prone. And sometimes there are multiple generated columns (ie computed fields etc). You as the developer should know what fields you can or want to get back.
As it is hard (or inefficient) to decide which fields to return, some drivers (by default) return all columns of the inserted (or deleted/updated) row. For example the PostgreSQL and Firebird drivers do that. On the other hand some drivers just return the last-generated key even if the table does not contain an identity column (I believe MySQL does, not 100% sure though). And I seem to remember that the Oracle driver simply returns the ROWID, leaving it up to the user to retrieve actual values from the database using that ROWID.
If you specifically know which columns you want returned, you can specify that yourself using the alternative methods that accept an array of column ordinal indices or column names. Although again not all drivers support that.
I am connecting to a Java DB database with JDBC and want to retrieve the id (which is on auto increment) of the last record inserted.
I see this is a common question, but I see solutions using for example MS SQL Server, what is the equivalent for Java DB?
No need to use a DBMS specific SQL for that.
That's what getGeneratedKeys() is for.
When preparing your statement you pass the name(s) of the auto-generated columns which you can then retrieve using getGeneratedKeys()
PreparedStatement pstmt = connection.prepareStatement(
"insert into some_table (col1, col2, ..) values (....)",
new String[] { "ID_COLUMN"} );
pstmt.executeUpdate();
ResultSet rs = pstmt.getGeneratedKeys(); // will return the ID in ID_COLUMN
Note that column names are case sensitive in this case (in Derby and many other DBMS).
new String[] { "ID_COLUMN"} is something different than new String[] { "id_column"}
Alternatively you can also use:
connection.prepareStatement("INSERT ...", PreparedStatement.RETURN_GENERATED_KEYS);
You may be able to get what you're looking for using the IDENTITY_VAL_LOCAL function. (Derby Reference)
This function is supposed to return "the most recently assigned value of an identity column for a connection, where the assignment occurred as a result of a single row INSERT statement using a VALUES clause."
It's worth noting that this function will return DECIMAL(31,0), regardless of the actual data type of the corresponding identity column.
Also, this only works for single row inserts that contain a VALUES clause.
For those who have issues getting the generated autoincrement id like I used to for Java Derby, my answer can be of help.
stmt.executeUpdate("INSERT INTO xx(Name) VALUES ('Joe')", Statement.RETURN_GENERATED_KEYS);
ResultSet rs = stmt.getGeneratedKeys();
if (rs.next()) {
int autoKey = rs.getInt(1); //this is the auto-generated key for your use
}
Answer copied from here
Is it possible to return the last row of a table in MS SQL Server.
I am using an auto increment field for the ID and i want to get the last one just added to join it with something else. Any idea?
Here's the code:
const string QUERY = #"INSERT INTO Questions (ID, Question, Answer, CategoryID, Permission) "
+ #"VALUES (#ID, #Question, #Answer, #CategoryID, #Permission) ";
using (var cmd = new SqlCommand(QUERY, conn))
{
cmd.Parameters.AddWithValue("#Question", question);
cmd.Parameters.AddWithValue("#Answer", answer);
cmd.Parameters.AddWithValue("#CategoryID", lastEdited);
cmd.Parameters.AddWithValue("#Permission", categoryID);
cmd.ExecuteNonQuery();
}
Not safe - could have multiple inserts going on at the same time and the last row you'd get might not be yours. You're better off using SCOPE_IDENTITY() to get the last key assigned for your transaction.
using an auto increment field ... and i want to get the last one just added to join it with something else.
The key here is "just added". If you have a bunch of different users hit the db at the same time, I don't think you want user A to retrieve the record created by user B. That means you probably want to use the scope_identity() function to get that id rather than running a query on the table again right away.
Depending on the context you might also need ##identity (would include triggers) or ident_current('questions') (limited to a specific table, but not the specific scope). But scope_identity() is almost always the right one to use.
Here's an example:
DECLARE #NewOrderID int
INSERT INTO TABLE [Orders] (CustomerID) VALUES (1234)
SELECT #NewOrderID=scope_identity()
INSERT INTO TABLE [OrderLines] (OrderID, ProductID, Quantity)
SELECT #NewOrderID, ProductID, Quantity
FROM [ShoppingCart]
WHERE CustomerID=1234 AND SessionKey=4321
Based on the code you posted, you can do something like this:
// don't list the ID column: it should be an identity column that sql server will handle for you
const string QUERY = "INSERT INTO Questions (Question, Answer, CategoryID, Permission) "
+ "VALUES (#Question, #Answer, #CategoryID, #Permission);"
+ "SELECT scope_identity();";
int NewQuestionID;
using (var cmd = new SqlCommand(QUERY, conn))
{
cmd.Parameters.AddWithValue("#Question", question);
cmd.Parameters.AddWithValue("#Answer", answer);
cmd.Parameters.AddWithValue("#CategoryID", lastEdited);
cmd.Parameters.AddWithValue("#Permission", categoryID);
NewQuestionID = (int)cmd.ExecuteScalar();
}
See my answer to another question here:
get new SQL record ID
The problem now is that you'll likely want subsequent sql statements to be in the same transaction. You could do this with client code, but I find keeping it all on the server to be cleaner. You could do that by building a very long sql string, but I tend to prefer a stored procedure at this point.
I'm also not a fan of the .AddWithValue() method — I prefer explicitly defining the parameter types — but we can leave that for another day.
Finally, it's kind of late now, but I want to emphasize that it's really better to try to keep this all on the db. It's okay to run multiple statements in one sql command, and you want to reduce the number of round trips you need to make to the db and the amount of data you need to pass back and forth between the db and your app. It also makes it easier to get the transactions right and keep things atomic where they need to be.
use
scope_identity() returns the last identity value generated in this session and this scope
ident_current() returns the last identity value generated for a particular table in any session and any scope
select ident_current( 'yourTableName' )
will return the last identity created by a different session.
Most of the time you should use scope_identity() right after an insert statement like so.
--insert statement
SET #id = CAST(SCOPE_IDENTITY() AS INT)
MSDN Link - Scope_Identity()
MSDN Link - Ident_Current
select top 1 * from yourtable order by id desc
I'm not sure of your version of SQL Server, but look for the OUTPUT clause of ther INSERT statement. You can capture a set of rows with this clause
Since the questioner is using .NET, here's a modified example of how to do it. (I removed ID from the insert list since it's autoincrement--the original example would fail. I also assume ID is an SQL int, not a bigint.)
const string QUERY = #"INSERT INTO Questions (Question, Answer, CategoryID, Permission) "
+ #"VALUES (#Question, #Answer, #CategoryID, #Permission);"
+ #"SELECT #ID = SCOPE_IDENTITY();";
using (var cmd = new SqlCommand(QUERY, conn))
{
cmd.Parameters.AddWithValue("#Question", question);
cmd.Parameters.AddWithValue("#Answer", answer);
cmd.Parameters.AddWithValue("#CategoryID", lastEdited);
cmd.Parameters.AddWithValue("#Permission", categoryID);
cmd.Parameters.Add("#ID", System.Data.SqlDbType.Int).Direction = ParameterDirection.Output;
cmd.ExecuteNonQuery();
int id = (int)cmd.Parameters["#ID"].Value;
}
EDITED: I also suggest considering LINQ to SQL instead of hand-coding SqlCommand objects--it's much better (faster to code, easier to use) for many common scenarios.
With a simple select you can do something like this:
SELECT *
FROM table_name
WHERE IDColumn=(SELECT max(IDColum) FROM table_name)
I'm having trouble with the following statement, which is returning the error "Sequence contains no elements":
var vUser = (from u in this.dcLAUNCHOnline.aspnet_Users
where u.UserName.Equals(this.wCreateUser.UserName)
select u).Single();
The SQL being generated:
SELECT [t0].[ApplicationId],
[t0].[UserId],
[t0].[UserName],
[t0].[LoweredUserName],
[t0].[MobileAlias],
[t0].[IsAnonymous],
[t0].[LastActivityDate],
[t0].[FirstName],
[t0].[LastName],
[t0].[Address_Street],
[t0].[Address_City],
[t0].[Address_Province],
[t0].[Address_Country],
[t0].[Address_PostalCode],
[t0].[Telephone_Main_AreaCode],
[t0].[Telephone_Main_Prefix],
[t0].[Telephone_Main_LineNumber],
[t0].[Telephone_Main_Extension],
[t0].[Telephone_Mobile_AreaCode],
[t0].[Telephone_Mobile_Prefix],
[t0].[Telephone_Mobile_LineNumber],
[t0].[Telephone_Mobile_Extension],
[t0].[Telephone_Other_AreaCode],
[t0].[Telephone_Other_Prefix],
[t0].[Telephone_Other_LineNumber],
[t0].[Telephone_Other_Extension],
[t0].[Gender],
[t0].[BirthDate]
FROM [dbo].[aspnet_Users] AS [t0]
WHERE [t0].[UserName] = #p0
-- #p0: Input NVarChar (Size = 20; Prec = 0; Scale = 0) [six.string#gmail.com]
-- Context: SqlProvider(Sql2008) Model: AttributedMetaModel Build: 3.5.30729.4918
When run in SQL server management studio, the script does return the row I expect (which is in the table)
I defined p0 with this line:
DECLARE #p0 NVarChar(20) = 'six.string#gmail.com'
Any ideas why this is failing? Thanks!
.Single() always fails if the collection it is called upon is empty or contains more than one element. SQL Server does not return any rows, which must be the case here.
You could either use .FirstOrDefault() or .SingleOrDefault() and check the return value against null, depending on whether you expect a single element to be returned by your query.
E.g. you have a unique constraint on the row "UserName" you filter upon, you should use .SingleOrDefault(). If null is returned, no row has been found. Multiple rows wil never be returned.
The InvalidOperationException that you are getting is thrown only when the query yielded no results.
The only thing that comes to my mind, (since you say that you are sure that the row exists on the database), is that you are maybe connecting to other database.
Check your DataContext's connection string and make sure you are querying the same database as in Management Studio.
Edit: BTW, you are querying directly the SqlMembershipProvider aspnet_Users table, to find users by UserName, you might want to give a look to the Membership.FindUsersByName method.