views:

569

answers:

4

I know I can update a single record like this - but then how to I get access to the id of the record that was updated? (I'm using MSSQL so I can't use Oracles RowId)

update myTable
set myCol = 'foo'
where itemId in (select top 1 itemId from myTable )

If I was peforming an Insert I could use getGeneratedKeys to get the id field value, but I don't think there is an equivalent for an update?

I know I can use a scrollable resultset to do what I want

i.e.

stmt = conn.prepareStatement("select top 1 myCol, itemId from myTable", ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
ResultSet resultSet = stmt.executeQuery();
if(resultSet.first()){
    resultSet.updateString(1, "foo");
    resultSet.updateRow();
    String theItemId = resultSet.getString(1)
}
resultSet.close();

but I'm concerned about performance as testing shows lock timeouts under load and I was wondering if there was a better/simpler way?

-- EDIT: Just to finalise this issue... When we migrate to MSSQL2005 we will upgrade our code to use Rich's answer. In the current release we have used the lock hints: (UPDLOCK ROWLOCK READPAST) to mitigate the performance problems our original code showed.

+3  A: 

This example works really well in MSSQL 2005...

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

DROP TABLE [dbo].[TEST_TABLE]
GO

CREATE TABLE [dbo].[TEST_TABLE](
    [id] [int] IDENTITY(1,1) NOT NULL,
    [name] [nvarchar](100) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
 CONSTRAINT [PK_TEST_TABLE] PRIMARY KEY CLUSTERED 
(
    [id] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]


-- An insert which will return the identity
INSERT INTO [dbo].[TEST_TABLE] ([name]) 
OUTPUT inserted.id
VALUES('Test 1')

-- Another insert which will return the identity
INSERT INTO [dbo].[TEST_TABLE] ([name]) 
OUTPUT inserted.id
VALUES('Test 2')

-- Now an update which will return the identity
UPDATE [dbo].[TEST_TABLE]
SET [name] = 'Updated Test 1'
OUTPUT inserted.id
WHERE [name] = 'Test 1'

SELECT id, [name] FROM [dbo].[TEST_TABLE]

And more specifically to your query...

update myTable
set myCol = 'foo'
output inserted.itemid
where itemId in (select top 1 itemId from myTable )
Rich Andrews
If you want to capture the value within the script (for example, to return it instead of it being output as a result set) create a local table variable ("declare @output table ( itemid int )") and do "output inserted.itemid into @output". You can then select it from there for further use.
GalacticCowboy
This example is exactly what I'm after... I'm currently stuck on MSSQL 2000 but the migration plan for 2008 is happening in March so I'll keep this up my sleeve for then. Thanks
A: 

For insert MSQSQL server has the @@Identity parameter which has the ID of the last inserted record.

Mg
deprecated, use scope_identity()
keithwarren7
and also doesn't work for updates - it's the last value *inserted*. @@Identity also has the drawback that, if some other event occurred elsewhere that inserted a new identity, you'll get that one instead of the one you want.
GalacticCowboy
A: 

I would do the following:

Begin Tran

update myTable
set myCol = 'foo'
where itemId in (select top 1 itemId from myTable )

select top 1 itemId from myTable

Commit Tran
GateKiller
Would this still work in a multi-threaded system under load?
A: 

Does this have to be in a single statement? Ultimately this would be best as a stored procedure

Create Procedure doMyUpdate

 @Id int output

as

Set @Id = (select top 1 itemId from myTable)
update myTable
set myCol = 'foo'
where itemId = @Id

another way would be to use th RETURN keyword and the built in RETURN_VALUE parameter...

Create Procedure doMyUpdate

as

Declare @Id int
Set @Id = (select top 1 itemId from myTable)

update myTable
set myCol = 'foo'
where itemId = @Id

RETURN @Id
keithwarren7
It doesn't have to be in a single statement. But it needs to work in a multi-threaded environment. Perhaps the trick is to create a unique key and use that as part of my update statement (i.e. to a new column). Then I can do the select with myUniqueCol = 'bar35636'?