Generate a unique value by Increment a column based on another column value - sql

I have to generate a unique code for each row by concatenating 2 values sequentially in SQL server. Eg: H1_123
Once the part2 (i.e 123) reaches 999, I have to increment part1 (i.e H1 to H2 ). So the desired output is H2_001, H2_002... H2_999, H3_001 etc. Please help me with this.
Edit: everytime a new row is inserting this unique code should be generated. The second part I can achieve through a sequencer, but how to increment the first part (H1) when the second part reaches 999?
If any suggestions without using loop will be helpful.

As I allude to in the comment, it seems like you are overly complicating this. You don't want to concatenate 2 values, you just need to do a little arithmetic to get the value you want. Just use a computed column like shown in the below example:
CREATE TABLE dbo.YourTable (ID int IDENTITY(1,1),
YourColumn AS CONCAT('H',(ID + 1000) / 1000, '_', RIGHT(CONCAT('000',ID),3)) PERSISTED);
GO
INSERT INTO dbo.YourTable
DEFAULT VALUES;
GO
INSERT INTO dbo.YourTable
DEFAULT VALUES;
INSERT INTO dbo.YourTable
DEFAULT VALUES;
GO
SELECT *
FROM dbo.YourTable;
GO
DROP TABLE dbo.YourTable
If you need to go from 999 to 001 the you could do the below, which is a little messy:
YourColumn AS CONCAT('H',(ID + ((ID-1) / 999)) / 1000 + 1, '_', RIGHT(CONCAT('000',ID + ((ID-1) / 999)),3)) PERSISTED
db<>fiddle

Related

max value in insert into

I need to get the maximum number in a column because in a insert operation I have to insert max number in that column +1 for each insert, I did this:
insert into table1(id ,.., field,...)
select newid(), ..., (SELECT CONVERT(VARCHAR(8), FORMAT((MAX(number)+1),'00000000'))
FROM table1)
It works just for the first inserted row, than I get this number for other rows too!
This is too long for a comment.
Why aren't you using an identity column? You can define the number as:
number int identity(1, 1)
If you want the value as a string padded to eight characters, then use a computed column:
number_string as (format(number, '00000000'))
EDIT:
There are strong reasons why you want an identity column and not to calculate the values yourself. You can do what you want using row_number(), where the logic looks like this:
insert into table1(id ,.., field,...)
select newid(), ...,
convert(varchar(8),
(coalesce(t1.max_number, 0) +
row_number() over (order by (select null))
)
)
from table2 t2 cross join
(select max(t1.number) as max_number from table1 t1) t1;
N
Note: I am assuming that the inserts are coming from a different table, but table2 can really be table1.
Very importantly: This is not thread safe. Two different threads can run the same code and result in the same values. The solution to this is locking the entire table. However, that can have very significant performance impacts.
There's a lot to comment on here, and like Gordon, I can't fit it into one.
Firstly, I notice you have a column id and you're inserted the value NEWID() into it. I therefore hope that id isn't your CLUSTERED INDEX, as if it is NEWID() is not doing it any favours. If you are using a uniqueindentifier for your CLUSTERED INDEX, use NEWSEQUENTIALID() instead, and don't provuide the value in the INSERT:
ALTER TABLE dbo.Table1 ADD CONSTRAINT DF_Table1_id DEFAULT NEWSEQUENTIALID() FOR id;
As for your INSERT, as I said in my comment: "Use an IDENTITY column. If you must then have sequential values, use a VIEW and ROW_NUMBER. Let SQL Server gracefully handle the incrementing value. Trying to increment the number yourself is going to only cause you problems, such as race conditions, and your data will be in a far worse position.". Unfortunately you can't change an existing column to an IDENTITY, so this is a little harder. Likely you'll want to do something like:
ALTER TABLE dbo.Table1 ADD number_new int IDENTITY(1,1);
GO
SET IDENTITY_INSERT dbo.Table1 ON;
--UPdate the new column with the existing values
UPDATE dbo.Table1
SET number_new = number;
SET IDENTITY_INSERT dbo.Table1 OFF;
GO
--Drop the old column and rename
ALTER TABLE dbo.Table1 DROP COLUMN number;
EXEC sp_rename N'dbo.Table1.number_new', N'number', N'COLUMN'
As Gordon said, if you simply then need a formatted value (with leading 0's) and no worry about gaps, use a computed column:
ALTER TABLE dbo.Table1 ADD Number_f AS RIGHT(CONCAT('00000000',number),8) PERSISTED;
If, however, you want them to be in sequential order, and update accordingly when a row is deleted, or a INSERT fails, etc, then you can use a view, with the following expression:
RIGHT(CONCAT('00000000',ROW_NUMBER() OVER (ORDER BY number ASC),8)
You can use a ROW_NUMBER() function to increment the IDs. Just replace the +1 with ROW_NUMBER() OVER(). Something like this:
insert into table1(id ,.., field,...)
select (SELECT MAX(number) FROM table1) + ROW_NUMBER() OVER(ORDER BY <field1>), ...
from table1
SQL Fiddle

How to have SQL increase ID by 1 on a column using an insert statement

I am trying to insert multiple rows into SQL. The table contains an external ID column that when using the APP increases this ID by one. The external ID is not the Primary key, but another ID in the table. Currently last external ID is 544. I want to insert 1600 additional rows and have the external ID increase by 1 for every row inserted. I have tried the following, but all of the external IDs end up being 100.
INSERT INTO tableA (externalid,tableuiduid)
VALUES ((select ISNULL(MAX(EXTERNALID) +1, 0)from tableA),newid());
I have also tried this, but it ends up inserting a duplicate external ID, as there are gaps in the numbers.
INSERT INTO tableA (ExternalID,tableAuid)
VALUES ((select count (externalid) + from tableA),newid());
Please let me know what I need to use to have this increase by 1 and not insert a duplicate ID.
The way you're doing it now, depending on your RDBMS, you can use an INSERT INTO ... SELECT statement:
INSERT INTO tableA (externalid, tableuiduid)
select ISNULL(MAX(EXTERNALID) + 1, 0), newid()
from tableA;
But you'll need to execute that 1600 times.
If you have an auxilliary number table or use a recursive CTE, you could use that to generate 1600 rows at once, but without knowing your RDBMS a precise implementation is very difficult.
You could define the field as an automatically incrementing field or sequence, but I get the impression that that isn't a good idea because you're not always going to be determining what the externalid value is.
You should use the +1 outside of ISNULL. And use INSERT INTO .. SELECT.
Try this way:
DECLARE #Cnt as int
SET #Cnt = 0
WHILE (#Cnt < 1600)
BEGIN
INSERT INTO tableA (externalid,tableuiduid)
select ISNULL(MAX(EXTERNALID),0) + 1,newid()
from tableA
SET #Cnt = #Cnt + 1
END
First create a sequence.
create sequence seq1
start with 544
increment by 1
maxvalue 99999;
Then insert.
INSERT INTO tableA (externalid)
VALUES (seq1.nextval())

Is there any editable Auto-Increment besdies IDENTITY?

The reason I need this for is that I made a column on my table called display_order, for now it's smallint and the numbers were pre-determined.
However, when I insert a new record with my software I don't know how to get the highest number in that column and add 1, so I thought about the possibility of an auto-incremented column where if I change 8 to 9 it will change everything else accordingly.
Is this possible?
The answer to your question is "No" IDENTITY is the only auto incrementing capability (and these columns are not updatable)
But if this is a display_order field can't you just make it float to allow you to insert items between other items rather than having to shift all other items down to create a gap?
However, when I insert a new record with my software I don't know how to get the highest number in that column and add 1,
Insert MyTable( display_order, .... )
Select (
Select Max(display_order) + 1
From MyTable As T1
), ...
From MyTable
However, I wouldn't recommend this. If display_order is user settable, then I would simply assume relative values. Thus, it wouldn't matter if a user added two values with a display_order = 0. If you really want to go the extra mile and provide the ability to resequence the display_order, you could do it like so:
Update MyTable
Set display_order = Z.NewSeq
From (
Select PKCol
, Row_Number() Over ( Order By display_order ) As NewSeq
From MyTable
) As Z
Join MyTable As T
On T.PKCol = Z.PKCol
Because you only get one IDENTITY column per table, I would probably use a trigger or other mechanism (if there's a centralized insertion stored proc) to default it to one more than the highest number in the table if not provided. This avoids having to SET IDENTITY_INSERT or anything like that.

select max value of a column in table with no rows

I am using oracle database
While inserting a row in a table, i need to find the max value of a column and increment it by 1, and use that value in row i am inserting.
INSERT INTO dts_route
(ROUTE_ID, ROUTE_UID, ROUTE_FOLDER)
VALUES (
(SELECT MAX(ROUTE_ID) + 1 FROM route) ,
ROUTE_UID,
ROUTE_FOLDER)
This works fine if their is at least one entry in table.
But returns null when their are no entries in table.
How can i get default value of 1 when their are no entries in table.
SELECT COALESCE(MAX(ROUTE_ID),0) ...
This is not a safe way of creating an auto-increment field. You can use an Oracle sequence to achieve this goal.
As for the null, you can use NVL to give a default value (say, 0) in case the function returns null.
Use sequence for the ID. You need to create sequence. See below link
http://www.basis.com/onlinedocs/documentation/b3odbc/sql_sequences.htm
Use:
INSERT INTO dts_route
(ROUTE_ID)
SELECT COALESCE(MAX(r.route_id), 0) +1
FROM ROUTE r
...but you really should be using a sequence to populate the value with a sequential numeric value:
CREATE SEQUENCE dts_route_seq;
...
INSERT INTO dts_route
(ROUTE_ID)
SELECT dts_route_seq.NEXTVAL
FROM DUAL;
Set a default for NULL
SELECT NVL(MAX(ROUTE_ID),0)
though using a sequence might be easier if you don't mind the odd gaps in your route ids
select 0 when null, then it will be 0+1 which is a correct number compared to null+1
SELECT isnull(MAX(ROUTE_ID),0) + 1 FROM route
If you are concerned about there being gaps in your route ids then create the sequence with the NOCACHE clause:
CREATE SEQUENCE dts_route_seq NOCACHE;
Note that there is a performance hit because Oracle now has to "commit" each time you increment the sequence.

SQL Identity with leading padded zeros

I have marked a column as Identity in my table
create table Identitytest(
number int identity(1,001) not null,
value varchar(500)
)
I need the identity column to be incremented as 001,002,003, etc.
The database shows that it is inserting as 1,2,3, etc.
How can this be done?
As the others have already rightfully pointed out - an INT never has leading zeroes - it just holds the value, that's all (and that's good that way).
If you need some additional formatting, you could always add a computed column to your table, something like:
ALTER TABLE dbo.Identitytest
ADD DisplayNumber AS RIGHT('000' + CAST(number AS VARCHAR(3)) , 3) PERSISTED
This way, your INT IDENTITY will be used as an INT and always contains the numerical value, while DisplayNumber contains 001, 002, ... 014, 015, ..... and so forth - automagically, always up to date.
Since it's a persisted field, it's now part of your table, and you can query on it, and even put an index on it to make queries faster:
SELECT value FROM dbo.IdentityTest WHERE DisplayNumber = '024'
And of course, you could use just about any formatting in the definition of your computed column, so you could also add a prefix or something:
ALTER TABLE dbo.Identitytest
ADD DisplayNumber
AS 'ABC-' + RIGHT('000' + CAST(number AS VARCHAR(3)) , 3) PERSISTED
So in this case, your DisplayNumber would be ABC-001, ABC-002, ... and so on.
You get the best of both worlds - you keep your INT IDENTITY which is numerical and automatically increased by SQL Server, and you can define a display format any way you like and have that available at any time.
If you want to display your number column with leading zeros, just pad it in your SELECT statement. It's a number, it will NOT store with leading zeros as an integer.
SELECT RIGHT('00000' + CAST([number] AS varchar(5)) , 3)
FROM IdentityTest
The 3 is the number of characters you want total in the output display.
If you require both the auto-incrementing number (which can only be a number) and an alphabetic representation of the number, you might consider looking at computed columns.
Here's a few links to get you going:
http://www.mssqltips.com/tip.asp?tip=1682
http://msdn.microsoft.com/en-us/library/ms191250.aspx
http://www.kodyaz.com/articles/sql-server-computed-column-calculated-column-sample.aspx
Why do you need that? As an integer, 001 is the same as 1. If what you want is that for display or other purposes, create another column and do your work there (you may do it as part of a trigger on the table, on insert, that looks at the newly inserted row, and creates the entry in the column appropriately.
i've got a table where i'm storing an integer, but the users want to see it a XXX, even if it has zeroes, so i wrote this code
declare #a int
set #a=1
select replicate('0',3-len(#a))+ cast(#a as varchar(4))
Here is another method:
create table TEST_T (ui int NOT NULL identity, name varchar(10))
insert into TEST_T values ( 'FRED' )
select NAME, ui, RIGHT('0000' + LTRIM(STR(ui)), 4) as ui_T from TEST_T
go
/* NOTE: A view could be created with a calculated column instead of the identity column. */
create view TEST_V as select NAME, RIGHT('0000' + LTRIM(STR(ui)), 4) as ui_V from TEST_T go
go
select * from TEST_V
drop view TEST_V
drop table TEST_T
Not quite as much data duplication(?) as adding a column to the table and no need to specify the column in the select statement.
I need the identity column to be
incremented as 001,002,003, etc.
The database shows that it is
inserting as 1,2,3, etc.
SQL databases store values, not the literals you used to write those values. 002 is 2. Just like 1 + 1 is 2. Would you expect SELECT 1 + 1 to display the string "1 + 1" instead of 2?
If you want the leading zeros to be stored in your column, you have to use a character type. But then you can't use AUTOINCREMENT/IDENTITY.
What you probably really want is something like printf("%03d", number) in program that reads from the database.