SQL Server - Reset custom identity column - sql

I have a table with two columns ID and SubDetail. I have created custom identity like 2018XX for the ID columns with 2018 as current year and XX as a number and it will start from "01". I have searched for some topic about "Reset identity field - SQL Server" but It only showed how to reset only number identity field. I'm confused that is there anyway to reset custom identity field.
Here is my SQL query code:
CREATE PROCEDURE spAddSubWareHouse
(#SubDetail VARCHAR(50))
AS
BEGIN
DECLARE #maxDetailID INT
SELECT #maxDetailID = MAX(CAST(RIGHT(ID, 2) AS INT))
FROM SubWareHouse
IF (#maxDetailID IS NULL)
SET #maxDetailID = 1
ELSE
SET #maxDetailID += 1
--insert
INSERT INTO SubWareHouse
VALUES (CAST(YEAR(GETDATE()) AS VARCHAR) + RIGHT('00' + CAST(#maxDetailID AS VARCHAR), 2), #SubDetail)
END

Don't bother doing this. Simply have a numeric identity column defined as:
id int identity(1, 1) primary key
You can then store the year as a separate column, because that appears to be information that you want.
Why do it this way? There are many reasons. First, identity is built into the database. So, identity columns are guaranteed to be unique even when multiple applications are doing inserts at the same time.
Second, integers are more efficient (slightly) for foreign key references.
Third, the locking required to do what you really want is very cumbersome and could significantly slow down your system. Plus, any implementation to prevent duplicates could have a bug -- so why not use the built-in mechanisms?
If you need to get the sequence number for a given year, it is easy enough using row_number():
select row_number() over (partition by year order by id) as year_sequence_number

Related

How can I auto-generate complex identifiers and ensure uniqueness?

I've been handed a very specific business requirement, and I'm having trouble translating it to work within our database.
We have multiple departments, who all deal with what we call files. Each file has a unique identifier, but how this identifier is assigned depends on the department that the file is associated with.
Departments ABC, DEF and GHI need the system to provide them with the next identifier following a pattern... while departments JKL and MNO need to be able to specify their own identifiers.
This is further compounded by the fact that the generated file numbers are semi complex. Their file numbers follow the pattern:
[a-z]{3,4}\d{2}-\d{4}
(A 3 or 4 letter prefix that corresponds to the department with two digits corresponding to the year, a dash followed by a four digit generated number - i.e. ABC13-0001)
The part before the dash is easy to generate... the file table has a mandatory foreign key reference to a department table that has the prefix column, and I can grab the year just as easily. it's the part after the dash that I can't seem to work out.
It's a four digit identifier. Each department needs the next generated ID to be as sequential to their department as possible (I say as sequential as possible, as I'm aware that even identity specifications can leave gaps). On top of that, we need to reset it back to 0001 each year. All of that, while ensuring that there are no duplicates, which is the most important part of all of this.
So, we only have one file table that is used by all of these departments. As such, to be able to handle JKL and MNO, I have the FileNumber field set to varchar(12) with a unique constraint. They can type in whatever they need, so long as it's unique. That just leaves me with how to generate the unique file numbers for the other departments.
My first instinct is to give the file table a surrogate identity primary key to make sure that, even if something goes wrong with the generated file number, that each record is guaranteed a unique identifier.
Then I would create a single row table that has two columns per department, one for a number and one for a date. The number would be the last used number for the 4 digit identifier suffix for a given department (as an int), and the date would be when it was assigned. Storing the date would let me check if the year has changed since the last id was pulled so that I can assign 1, instead of lastid + 1.
Then an insert trigger on the file table would generate the file id using:
The foreign key to the department table to get the department prefix
CONVERT(VARCHAR, YEAR( GETDATE() ) % 100) to pull the current two digit year
A select to the above described utility table to get the last id + 1 (or reseting to 1 if the current year differs from the year of the last updated date).
The trigger would finally update the utility table with the last used id and date.
In theory, I think that would work... and the unique constraint on the file id column would prevent an insert where the generated file id already exists. But it feels so brittle, and I can foresee that unique constraint being a double edged sword that would possibly prevent a department from creating a new file should the trigger fail to update the utility table. After all, if it doesn't, then the next time a file number is generated, it would try to use the same generated id, and fail. There must be a better way.
My other thought was to have a table per department with just an identity integer column and non-null date field with a default of (getdate())... and have the trigger on the file table insert a new row, and use that id. It would also be responsible for deleting all rows in the given department id table and reset the identity come the new year. It feels more secure to me, but then I have 5 utility tables (1 per department that auto-generate ids) with up to 9999 records, just to generate an id.
Am I missing something simple? Am I going about this the right way? I just can't help but think that there must be an easier way. Am I right to try to make the SQL server responsible for this, or should I try doing this in the desktop application that I will build on top of this database?
So, I think it's possible you're overcomplicating this. It's also possible I'm misunderstanding the requirements. I don't believe you need separate tables or anything, you just need to check the existing IDs and increment them. SQLFiddle doesn't seem to be working right now, but here's a method I came up with in my local DB:
create table dept_ids (
id varchar(12) primary key
)
insert into dept_ids values('ABC13-0001')
insert into dept_ids values('DEF13-0001')
insert into dept_ids values('GHI13-0001')
insert into dept_ids values('JKL13-0001')
insert into dept_ids values('ABC13-0002')
declare
#dept varchar(4)
, #year varchar(2)
, #prefix varchar(8)
, #new_seq int
set #dept = 'ABC'
select #year = right(cast(DATEPART(yy, getdate()) as varchar(4)), 2)
set #prefix = #dept + #year + '-'
select #new_seq = isnull(right(max(id), 4), 0) + 1 from dept_ids where id like #prefix + '%'
select new_id = #prefix + right(replicate('0', 4) + cast(#new_seq as varchar(4)), 4)
This handles the different departments of course, as well as the year scenario by using getdate() and an ISNULL check. For the departments that can enter their own sequence values, you can just add a null check for that parameter and skip generating this value if it's present.
If I'm oversimplifying, feel free to let me know and I'll adjust.

Generate sequence number in SQL on every insert

I need to enter primary key in table as in below format
YYYY/MMM/NNNNNN
Where, YYYY is the current year, MMM is Month , and NNNNNN is a sequence no from 000001, 000002, 000003, .... 999999.
So my primary key will look like 2012/Oct/000001 or 2012/Oct/000010 ....
How can I generate this type of code..
I can get Year and Month from Getdate() function. But how can I manage sequence number on every insert. can you please give me logic for that?
By far the easiest way would be to let SQL Server handle the dishing out of consecutive numbers using an INT IDENTITY column - and then use a trigger to create that specific format that you need, in a separate column.
So given this table:
CREATE TABLE SampleTable
(ID INT IDENTITY, SaleDate DATE, ComputedPK VARCHAR(25) )
you could use a trigger like this to compute the ComputedPK from the ID (autonumber, handled by SQL Server) and the SaleDate as the date column:
CREATE TRIGGER trgSampleTableInsert
ON dbo.SampleTable FOR INSERT
AS
UPDATE dbo.SampleTable
SET ComputedPK = CAST(YEAR(i.SaleDate) AS CHAR(4)) + '/' + DATENAME(MONTH, i.SaleDate) + '/' + RIGHT('000000' + CAST(i.ID AS VARCHAR(6)), 6)
FROM dbo.SampleTable s
INNER JOIN INSERTED i ON s.ID = i.ID
This approach will not start at 1 for each month, however - but do you really need that? Isn't a sequential number (even across months) good enough?
Update: of course, if you're using SQL Server 2012 (you didn't specify which version of SQL Server you're using...) - you could use a SEQUENCE object to handle the consecutive numbering - and you could even reset that sequence to 1 again every start of a month ....
SELECT
CONCAT(
DATEPART(YEAR, GETDATE()),
'/',
DATENAME(MONTH,GETDATE()),
'/',
REPLACE(STR(((SELECT COUNT(*) FROM yourtable WHERE monthname = DATENAME(MONTH,GETDATE()) GROUP BY monthname) + 1),6,0),' ','0')
)
)
This is untested now tested. You would have to add a monthname column (there is a way of doing this without adding a column, but this is the most convenient)
You can also cast and use addition if you don't want to rely on concat. http://sqlfiddle.com/#!6/3e43d/6
Why don't you just consider a compound key with an identity/numeric column and a date column (or if the day is not needed, the year/month)?
This would give you the same behaviour and would probably be a bit simpler to implement/maintain
I still would like to know the reasoning behind this - just so we can make a better educated guess
Set your Primary Key on two columns. One of them will represent the Daten and the other one the Serial number. Set a Sequence on the Serial column which increases automatically. This will make it easier for you filter the Date part of the Key.

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.

SQL Server identity counterpart problem

I'm using SQL Server and I want to use identity constraint in it
I know how to use it in following manner
create table mytable
(
c1 int primary key identity(1,1);
)
the above code works fine but what if i want the identity column to have values as EMP001, EMP002,... instead of 1,2....
Thanks in advance,
Guru
Identity columns can only be integers. If you want it to "look like" EMP001, EMP002, etc then that's simply a display issue and not a storage one (that is, you don't need to store "EMP001", "EMP002" etc, just store it as 1, 2, 3 via a normal identity column and display it in your application as "EMP001", "EMP002", etc)
Create a computed column that concatenates like this:
'EMP' + RIGHT('00' + CAST(c1 AS varchar(3)), 3)
Otherwise an IDENTITY column as surrogate key is just that: a meaningless number.
I assume you're only going to have 999 rows or is there another sequence somewhere?

SQL IDENTITY COLUMN

I have an sql table which is basically a statement.
Now lets say the records I have in my table have a date and an identity column which is autonumbered and defines the order which the transactions are displayed in the front end to the client.
The issue is during an insert some of the data have gone missing and some transactions between two dates are missing.
I need to insert the data into the table, but I need to insert them between the dates and not at the end of the table. If I do a a normal insert, the data will appear at the end of the table and not at the date I specify, because the identity column is autonumbered, and cannot be updated.
Using SET IDENTITY_INSERT (table) ON, you force SQL Server to let you insert any arbitrary value into an IDENTITY column - but there's no way to update an IDENTITY column.
What's the big problem with a few gaps anyway?? Yes, it might be a bit of a "cosmetic" problem - but how much hassle and effort do you really want to spend on cosmetic problems?? The order of the entries is still a given - even with gaps.
So again: what's the big deal?? IDENTITY columns are guaranteed to be ever increasing - that's all they guarantee. And for 99% of the cases, that's more than good enough....
Why not just display the records in the user interface sorted by the date, rather than by the primary key?
OK, if you really want to do this (personally, I think changing the sort date in the UI is going to be easier than updating the primary key values in the database, but anyway...). This should work, assuming you're not using the primary key values in any foreign key constraints (if you are, then you'll need to make sure those constraints have ON UPDATE CASCADE set)
SET IDENTITY_INSERT tablename ON
UPDATE tablename SET
primary_key = primay_key + 1
WHERE
primary_key >= <the primary key where you want to insert the new date>
INSERT INTO tablename
(primary_key, date, ...)
VALUES
(<the primary key to insert>, <the date to insert>, ...)
SET IDENTITY_INSERT tablename OFF
However, I strongly, strongly suggest you backup your database before attempting this.
Just out of curiosity, is it one ID per date? Your answers imply this a little, so if so, replace the Identity column with a computed column that is defined as the date difference in days from an arbitrary starting point?
DECLARE #EXAMPLE TABLE
(
[Date] DATE,
ID AS DATEDIFF(Day, '1 Jan 2010', [Date])
)
INSERT INTO #EXAMPLE([Date])
VALUES (GETDATE()), (GETDATE()+1), (GETDATE()+2)
SELECT * FROM #EXAMPLE