June 21, 2004 at 10:49 am
Hi,
Does anyone know how to get the value of a field and update it in one statement? I know it can be done, but I can't remember how. For example I'm currently doing
select intcount from table1 where loc = 'A'
update table1 set intcount = intcount + 1 where loc = 'A'
I want one statement that returns intcount and then updates intcount by 1.
Thanks
Mark
June 21, 2004 at 12:38 pm
I'm a little confused by the terminology of the question. As written above to actually return the value of intcount to a process outside the statement a third statement would be needed:
select intcount from table1 where loc = 'A'
after it has been updated.
The statement
update table1 set intcount= intcount +1 where loc='A'
will increment the value of intcount by one but will still require another statement to actually return the value to any process
June 21, 2004 at 2:12 pm
I want to do this in one TSQL statement.
June 22, 2004 at 2:32 am
Hi,
You can do that from the following statement.
update table1 set intcount = (select a.intcount +1 from table1 a where a.loc='A') where loc = 'A'
Enjoy!!
June 22, 2004 at 3:51 am
So, if I understand it correctly, what you want is one-statement incrementing counter-thingy...?
declare @nextid int
update table1
set @nextid = intcount = intcount + 1
where loc = 'A'
@nextid now contains the incremented value.
/Kenneth
June 22, 2004 at 8:15 am
If you are going to be using this code regularly it might be worth creating a function like:
create function fnIntCount
(@location varchar(1))
RETURNS INT
AS
BEGIN
declare @nextNumber int
select @nextNumber = intcount from table1 where location = @location
update table1 set intcount = @nextNumber
RETURN(@nextNumber)
END
You can then run a single line to update the value wherever you want to call it from
i.e.
Update table1 set intcount = select fnIntCount('A')+1
June 22, 2004 at 7:25 pm
You can't do this in a function, as you can't issue "Update" commands on any tables (except table variables).
Also, using the syntax Kenneth proposed (which is good, although using an identity column and @@Identity is better)
declare @nextid int
update table1
set @nextid = intcount = intcount + 1
where loc = 'A'
Select @nextid
Remember that your variable can only hold one value at a time, so if you update multiple rows then only the last row updated will set the variable. There is a hack to get around this, although it is not elegant:
if object_ID('tempdb..#Test1') is not null drop table #test1
create table #Test1 (Test1 varchar(25))
Insert #Test1 Values ('A')
Insert #Test1 Values ('B')
Insert #Test1 Values ('C')
declare @test1 varchar(100)
select @Test1 = ''
Update #Test1
set Test1 = Test1 + Test1,
@Test1 = @test1 + Test1 + Test1
select @Test1
Signature is NULL
June 23, 2004 at 1:26 am
Actually, using identity column and @@identity opens up for some caveats. IF any triggers should be involved in the scope of the transaction on other tables with ident columns, @@identity will not be what you expect.
Using the 'hard' way of storing the counter in a table along wiht a qualifier as to which counter you want to use, is a simple way of rolling your own fully controllable counter-mechanics.
And if you consistently use the update method accessing it, you won't risk two connections getting hold of the same value either.
The update construct is intended for this use - getting the 'next ID' for whatever further use..
Here's a simple way of implementing it as a stored proc
-- Script downloaded 6/23/2004 2:24:00 AM
-- From The SQL Server Worldwide User's Group (www.sswug.org)
-- Scripts provided AS-IS without warranty of any kind use at your own risk
if object_id('getNextID') is not null drop proc getNextID
go
create proc getNextID @tabname sysname, @nextid int OUTPUT
as
--file:getNextID.sql
--why:generic proc that returns the next avilable id-counter for the specified table
--without the possibility for concurrent users to recieve the same number.
-- (the update method may not be supported by Microsoft, it does however work)
-- use at your own risk.
--
--Usage:
--declare @varForNewId int
--exec getNextID 'tableName', @varForNewId OUTPUT
--@varForNewId now contains the new id....
--table:uniqueIdxxUx- table to keep the counter in - 1 row for each counter and table
--
--by:Kenneth Wilhelmsson
--when:2001-02-09- first version
set nocount on
declare @err int,
@rc int
-- check that counter for this table exists
if not exists ( select * from uniqueId where tablename = @tabname ) goto errhandler
-- get the next id
update uniqueId
set @nextid = nextId = nextId + 1
where tablename = @tabname
select @err = @@error, @rc = @@rowcount
if (@err 0) goto errhandler
if (@rc 1) goto errhandler
return @err
errhandler:
if ( @@trancount > 0 ) ROLLBACK TRANSACTION
declare @errmsg varchar(255)
set@errmsg = 'procedure: ' + object_name(@@procid) + ' *** FATAL ERROR *** '
raiserror(@errmsg, 16, -1) with log
return @err
go
/*
example tabdef:
create table uniqueId
(tablename sysname not null,
nextidint not null
)
alter table uniqueId add constraint PK_uniqueId_ucix primary key clustered (tablename)
*/
=;o)
/Kenneth
June 23, 2004 at 8:55 am
Apologies for my code with the update inside the function. You cannot do this, but you can update the table from a value called from a function - which was my intention.
This will then give you one line of code only.
Revised function:
create function fnIntCount
(@location varchar(1))
RETURNS INT
AS
BEGIN
declare @nextNumber int
select @nextNumber = intcount from table1 where location = @location
RETURN(@nextNumber)
END
You can then run a single line to update the value wherever you want to call it from
i.e.
Update table1 set intcount = (select fnIntCount('A')+1).
I have tried this and it does work!
June 23, 2004 at 12:34 pm
if you're worried about triggers screwing with @@Identity, use the function SCOPE_IDENTITY( ) instead. Or you can use IDENT_CURRENT('table_name')...
Lot's of options for attaining the last identity column...it's a nice feature of SQL server that makes things easier most of the time.
cl
Signature is NULL
June 24, 2004 at 12:14 am
What happens if two connections use this function at the same time..? They would both get the same number, it seems.
Usually that isn't too good.
When you get to the point that you need to roll your own counter-thingy, it's usually becuase you want control over id-numbers used and generated. And in those circumstances, duplicates would probably be less desirable than plague.
So, then there are two ways of doing that - making sure that one and only one connection can get any given unique value.
1) The single update method shown above.
2) Using two statements - increment (update) and then select the new value.
Now, if choosing the latter, there are a few things that is important.
Order matters.
You must make sure noone else sneaks in between and grabs a number that is 'in transit' - ie already taken but not yet incremented.
So, if doing this, then this is the sequence;
(this assumes that the isolation level is set to READ COMMITTED at a minimum - if it is READ UNCOMMITTED (ie 'dirty reads') - then this 'two-step' method will never work securely)
1) BEGIN TRAN
2) UPDATE oldvalue to newvalue
3) SELECT newvalue (se we can make use of it)
4) COMMIT
Yes, it is important that the update comes first, before the select. If you do it the other way around, someone else may come and select the same value before the update happens.
But, in retrospect, maybe it's simpler with the single update-increment-and-hand-me-the-new-value-in-one-go way after all..?
It's shorter, behaves the same regardless of isolation level, doesn't need to be enclosed in an explicit transaction to ensure integrity...
/Kenneth
June 24, 2004 at 1:11 pm
If you absolutely must have a unique stamp for each row, then simply use the uniqueidentifier data type.
cl
Signature is NULL
June 28, 2004 at 1:18 am
Uniqueidentifier would however use 16 bytes, when an int only uses 4, and the format is less than 'user-friendly'.
Personally, I'm not too fond of using uniqueidentifier as PK if it can be avoided, mainly because it's cumbersome.
/Kenneth
Viewing 13 posts - 1 through 12 (of 12 total)
You must be logged in to reply to this topic. Login to reply