MAX/ORDER BY on char column
in my SQL Server 2005 database I have a column RMA_Number
with datatype char(10) in table RMA
.
The value is an increasing number with the format RMA0002511
. What is the fastest way to get the highest number to increment it on inserting?
My first approach was:
SELECT TOP (1) RMA_Number
FROM RMA
WHERE (RMA_generated = 1)
ORDER BY Creation_Date DESC
But this was error-prone because it was somehow possible that a higher R开发者_如何学GoMA_Number has an earlier creation date. As a workaround, sorting by the primary key works:
SELECT TOP (1) RMA_Number
FROM RMA
WHERE (RMA_generated = 1)
ORDER BY idRMA DESC
But maybe this is also a possible source of error.
Logically the best way would be to ORDER BY RMA_Number DESC
.
But because I was not sure if this gives always the correct result and thought that sorting a char column could get slow if the number of records increase, I chose to order by the Date column.
So,
- is it a good idea to order by a char(10)-column (performance and accuracy)?
- would it be better to
SELECT MAX( RMA_Number ) FROM RMA
to get the highest number(perf. and accuracy) - should I stick on using the primary key to order by if the first two points are wrong or should I use an
int
column and format the number in the application?
EDIT:
I think I must clarify something that I haven't mentioned. The RMA_Number is not generated on every insert. So maybe there are many records without a number. Martin uses the primary key to build the number. That would be a problem, because the gaps would be too big.
Thank you in advance.
The fastest and safest (for concurrency) way would be to not store the RMA000...
prefix at all.
Just create an integer identity column and add the prefix on via a computed column.
create table #RMA
(
id int identity(2511,1) primary key,
RMA_Number as 'RMA' + RIGHT('000000' + CAST(id as varchar(7)),7)
)
insert into #RMA
default values
select * from #RMA
Or following the new info that not all records have an RMA_Number
you could use this approach for a non blocking, efficient, and concurrency safe solution.
CREATE TABLE dbo.Sequence(
val int IDENTITY (2511, 2) /*Seed this at 1 + whatever your current max value is*/
)
GO
/*Call this procedure to get allocated the next sequence number to use*/
CREATE PROC dbo.GetSequence
@val AS int OUTPUT
AS
BEGIN TRAN
SAVE TRAN S1
INSERT INTO dbo.Sequence DEFAULT VALUES
SET @val=SCOPE_IDENTITY()
ROLLBACK TRAN S1 /*Rolls back just as far as the save point to prevent the
sequence table filling up. The id allocated won't be reused*/
COMMIT TRAN
First off, you're looking at a serious race-condition.
When we needed this on a project we worked with, we had a separate table with the current value stored in it and a function to generate the next one. We implemented locking to keep multiple calls to get the next number. As I recall, this was because we had to use alpha-numeric identity numbers (the function took care of the complex incrementing of that).
However, I like @Martin's solution best: use and IDENTITY
field. You can either drop the prefix, as he suggests, or you can simply drop it from the column and append it back on when SELECT
ing from the table.
精彩评论