SQL Server: auto-generated custom format sequence number - sql

I am working with Microsoft SQL Server 2014. In our requirement, custom formatted sequence number is include.
The sequence number format is CAT-YYYY-MM-NNNNNN. Sample data:
CAT-2016-10-000001
CAT-2016-10-000002
.
.
.
CAT-2016-10-999999
I don't want to use GUID or any other and I want to work with a procedure or function.
So, I am trying with this:
CREATE TABLE [category]
(
[id] int NOT NULL UNIQUE IDENTITY,
[category_no] nvarchar(20) NOT NULL,
[category_name] nvarchar(50) NOT NULL,
PRIMARY KEY ([id])
);
CREATE FUNCTION generate_category_no()
RETURNS CHAR(20)
AS
BEGIN
DECLARE #category_no CHAR(20)
SET #category_no = (SELECT MAX(category_no) FROM category)
IF #category_no IS NULL
SET #category_no = 'CAT-' + YEAR(getDate()) + '-' + MONTH(getDate()) + '-000001'
DECLARE #no int
SET #no = RIGHT(#category_no,6) + 1
RETURN 'CAT-' + YEAR(getDate()) + '-' + MONTH(getDate()) + '-' + right('00000' + CONVERT(VARCHAR(10),#no),6)
END
GO
ALTER TABLE category DROP COLUMN category_no;
ALTER TABLE category ADD category_no AS dbo.generate_category_no();
INSERT INTO category (category_name)
VALUES ('BMW'), ('JAGUAR');
When I run the above SQL in step-by-step, it is OK. It shown no error. But when I run the following command:
SELECT * FROM category;
it shows the following error:
Msg 217, Level 16, State 1, Line 1
Maximum stored procedure, function, trigger, or view nesting level exceeded (limit 32).
I don't know how to solve this one. And even I don't know my function has worked or not. I referenced from internet for this function.
ADDED
I need to reset sequence no for every month. Eg. for next month, no should be as follow:
CAT-2016-11-000001
Please, enlighten me. Thanks in advance!

Modify your function as below
ALTER TABLE category DROP COLUMN category_no;
alter FUNCTION dbo.generate_category_no( #id int)
RETURNS CHAR(20)
AS
BEGIN
RETURN 'CAT-' + cast(YEAR(getDate()) as varchar(10)) + '-' + cast(MONTH(getDate()) as varchar(10))+ '-' + right('00000' + CONVERT(VARCHAR(10),#id),6)
END
ALTER TABLE category ADD category_no AS dbo.generate_category_no(id);
INSERT INTO category
(category_name)
VALUES
('BMW13'),
('JAGUAR');
SELECT * FROM category will give the below result.
1 BMW CAT-2016-10-000001
2 JAGUAR CAT-2016-10-000002
3 BMW1 CAT-2016-10-000003
4 BMW13 CAT-2016-10-000004

Try this:
To initialize your new field:
ALTER TABLE category DROP COLUMN category_no;
ALTER TABLE category ADD category_no CHAR(20)
UPDATE category set category_no = dbo.generate_category_no()
For other insert:
CREATE TRIGGER [dbo].[category_i]
ON [dbo].[category]
AFTER INSERT
AS BEGIN
UPDATE category
SET category_no = dbo.generate_category_no()
FROM inserted
WHERE category.pk = inserted.pk
END
But you can try to use SEQUENCE feature, available on Sql Server by 2012 version
About SEQUENCE you can see here

Biggest flaw in your function is it will not work for Batch Insert's
Since you have ID auto generated, here is a easier way to do this
category_no AS Concat('CAT-', Year(Getdate()), '-', Month(Getdate()), '-', RIGHT(Concat('00000', id), 6))
Demo
CREATE TABLE #seq
(
id INT IDENTITY(1, 1),
name VARCHAR(10),
category_no AS Concat('CAT-', Year(Getdate()), '-', Month(Getdate()), '-', RIGHT(Concat('00000', id), 6))
)
INSERT INTO #seq
(name)
VALUES ('val')
Result :
id name category_no
-- ---- -----------
1 val CAT-2016-10-000001

Finally, I solved the problem. My Function look like as follow:
CREATE FUNCTION generate_category_no()
RETURNS CHAR(20)
AS
BEGIN
DECLARE #category_no CHAR(20)
SET #category_no = (SELECT MAX(category_no) FROM category WHERE category_no LIKE CONCAT('CAT-', YEAR(getDate()), '-', MONTH(getDate()), '-%'))
IF #category_no is null SET #category_no = CONCAT('CAT-', YEAR(getDate()), '-', MONTH(getDate()), '-000000')
DECLARE #no INT
SET #no = RIGHT(#category_no,6) + 1
RETURN CONCAT('CAT-', YEAR(getDate()), '-', MONTH(getDate()), '-', RIGHT('00000' + CONVERT(VARCHAR(10),#no),6))
END
GO
And I insert data as follow:
INSERT INTO category (category_no, category_name) VALUES (dbo.generate_category_no(),'BMW');
INSERT INTO category (category_no, category_name) VALUES (dbo.generate_category_no(),'JAGUAR');
One things is that We can call function from INSERT query.
So, when I run the following sql:
SELECT * FROM category;
It give the result as shown in below.
+---+--------------------+--------------+
|id |category_no |category_name |
+---+--------------------+--------------+
| 1 |CAT-2016-10-000001 | BMW |
| 2 |CAT-2016-10-000002 | JAGUAR |
+---+--------------------+--------------+
Thanks everybody for helping me. Thanks!!!

Related

Formatted sequence

I need to make a SQL Server sequence formatted like this VT-000/2015,
where the zero's is the bill number which increments every record, starting from one in same year which is 2015
e.g:
VT-001/2014,
VT-002/2014,
VT-003/2014,
VT-004/2014,
VT-001/2015,
VT-002/2015
How can I make this?
Materializing the row number in the table is problematic. I think a view is the right tool for this job.
Here is one way to do this by creating a view and using ROW_NUMBER():
CREATE TABLE PRODUCT (PRODUCT_ID int,YEAR INT);
CREATE VIEW dbo.VIEW_PRODUCT AS
SELECT
PRODUCT_ID,
[YEAR],
ROW_NUMBER() OVER(PARTITION BY [YEAR] ORDER by [YEAR]) AS RowNum,
'VT-' + RIGHT(replicate('0', 3)
+ cast(ROW_NUMBER() OVER(PARTITION BY [YEAR] ORDER by [YEAR]) AS VARCHAR(10)), 3)
+ '/' + cast(YEAR AS VARCHAR(4)) as SERIAL
FROM PRODUCT;
INSERT INTO PRODUCT VALUES
(1000,2014),
(2000,2014),
(3000,2015),
(4000,2015),
(5000,2015);
SQL Fiddle Demo
If you're on SQL Server 2012, you can use sequences and a computed column - like this:
Step 1: define your sequence
CREATE SEQUENCE dbo.YearlyNumbers
START WITH 1
INCREMENT BY 1 ;
GO
You can reset that sequence back to 1 every January 1 of a new year, to get new numbers starting at 1, for the new year
Step 2: define your table, include a computed column that concatenates together the various bits and pieces:
CREATE TABLE dbo.Product
(
ProductID INT IDENTITY(1,1) PRIMARY KEY CLUSTERED,
ProductDate DATE,
-- default constraint gets a new value from the sequence on every insert
SequenceValue INT
CONSTRAINT DF_Product_SeqValue DEFAULT(NEXT VALUE FOR YearlyNumbers),
-- computed column puts together all bits to create the formatted ID you're looking for
FormattedID AS 'VT-' + RIGHT('0000' + CAST(SequenceValue AS VARCHAR(4)), 4) +
'/' + CAST(YEAR(ProductDate) AS VARCHAR(4)) PERSISTED
)
Now, when you insert values into your table:
INSERT INTO dbo.Product (ProductDate)
VALUES ('20150115'), ('20150331'), ('20150222'), ('20150414'), ('20150526')
the INSERT will fetch values from the sequence, and the computed column shows the formatted ID as requested:
SELECT * FROM dbo.Product
Consider your table is like this:
CREATE TABLE PRODUCT ([SERIAL] NVARCHAR(30), [YEAR] INT, [COUNTER] INT)
Here is the procedure you can use:
CREATE PROCEDURE PRODUCT_INSERT
#YEAR INT
AS
BEGIN
DECLARE #COUNTER INT
SELECT #COUNTER = ISNULL(MAX([COUNTER]), 0) + 1 FROM PRODUCT
WHERE [YEAR] = #YEAR
INSERT INTO PRODUCT
VALUES ('VT-' + REPLACE(STR(#COUNTER, 3), SPACE(1), '0') + '/' + CONVERT(nvarchar(4),#YEAR), #YEAR, #COUNTER)
END

how to generate a number following a pattern using sql?

I want to generate a pattern in SQL Server.
e.g. PA0001
and increment it every time (meaning if PA0001 has already been generated the next should be PA0002 and so on).
I have no idea how to go about this!
Can anyone help?
The canonical way is to have an indentity column and then create this code as a computed column:
create table . . . (
inc int not null identity(1, 1),
. . .
code as ('PA' + right('0000' + cast(inc as varchar(255)), 4)
This only works with one prefix -- if you want all values to start with 0001. If you have multiple prefixes, you can:
Use a trigger.
Calculate the code in queries.
Change your requirements so you can use codes that do not have other meanings.
You can write as:
create table Test
(
ID int identity not null primary key,
pattern varchar(100)
)
Go
create function Nextpattern (#id int)
returns varchar(20)
as
begin
return 'PA' +
CONVERT(VARCHAR(10), GETDATE(), 110) + right('00' + convert(varchar(10), #id), 2)
end
--Solution 1:
Go
alter table Test add Newpattern as dbo.Nextpattern(ID)
Go
insert into Test values (1)
Go
--solution2:
-- not good as what if two processes attempt to
-- add a row to the table at the exact same time?
create function dbo.fnNextpattern()
returns varchar(20)
as
begin
declare #lastval varchar(20)
set #lastval = (select max(pattern) from Test)
if #lastval is null set #lastval = 'PA' +
CONVERT(VARCHAR(10), GETDATE(), 110) + '01'
declare #i int
set #i = right(#lastval,2) + 1
return 'PA' +
CONVERT(VARCHAR(10), GETDATE(), 110)+ right('00' + convert(varchar(10),#i),2)
end
go
DEMO

Insert column value depending on an autoincrement column

I have table in my SQL Server database with following columns
ID | NAME | DOB | APPLICATION NO |
1 | JOHN | 31/05/1986 | KPL\2014\1 |
2 | MARY | 26/04/1965 | KPL\2014\2 |
3 | VARUN | 15/03/1972 | KPL\2014\3 |
Here column ID is an auto increment column and column APPLICATION NO is dependent on ID.
That means APPLICATION NO is the concatenation of KPL\, YEAR and column value of ID.
Then how can I make an insert query?
Why don't you use a computed column ?
I would change the table's definition.
add a "year" column
add an "application_name" column (if it's not always "KPL").
then create your computed column, with the needed fields
alter table <yourTable> add computed_application_name as (application_name + '/' + CAST(<yearColumn> as VARCHAR(4) + '/' + <otherColumn> + CAST(id as VARCHAR(MAX))
Just use a computed column:
alter table t
add application_no as ('KPL' + cast(year(getdate()) as varchar(255)) + cast(id as varchar(255));
It occurs to me that you really want the year when the row was inserted. For that purpose, I would recommend adding a createdat column and then using that instead of getdate():
alter table t add CreatedAt datetime default getdate();
If you have data in the table, then set the value (this is not needed if the table is empty):
update table t set CreatedAt = getdate();
Then define application_no:
alter table t
add application_no as ('KPL' + cast(year(CreatedAt) as varchar(255)) + cast(id as varchar(255));
Simply use a computed column for 'APPLICATION_NO':
create table tbl (
ID int,
NAME varchar(10),
DOB date,
APPLICATION_NO as ('KPL\'+cast(year(dob) as char(4))+'\'+cast(id as varchar))
)
You can further persist and index it as well.
You can create a trigger to run after insert. In your insert, ignore application_no. And your trigger update this column based on ID.
Something like this:
CREATE TRIGGER [TRIGGER_TABLE1] ON TABLE1
AFTER INSERT
AS
BEGIN
UPDATE TABLE1
SET APPLICATION_NO = 'KPL\' + CONVERT(VARCHAR, YEAR(GETDATE())) + '\' + CONVERT(VARCHAR, ID)
WHERE APPLICATION_NO IS NULL
END
EDIT: This way, you should be able to use other columns values as well. APPLICATION_NO just need to accept NULL values. So you control like this what will be updated or not.
Use SCOPE_IDENTITY get the inserted record Id
Declare #Id INT
Insert Into TABLENAME(NAME ,DOB )
Values('Name','1/1/1990')
Set #Id=SCOPE_IDENTITY()
IF(#Id>0)
begin
declare #col varchar(100)=''
set #col= 'KPL\' + CONVERT(varchar(4),YEAR(GETDATE()))+'\' + CONVERT(varchar(4),#Id)
Update TABLENAME
SET APPLICATIONNO= #col
Where ID = #id
end
If you really want to do it by yourself with an insert statement:
assuming that you want to insert data from another table
and TABLE1 is the table you described in your question and TABLE2 would be the table you want to import data from:
DECLARE #MAXID INT
SELECT #MAXID = MAX(ID) FROM TABLE1
INSERT INTO TABLE1
(NAME,DOB,APPLICATION_NO)
SELECT
NAME,
DOB,
'KPL\'
+ CONVERT(CHAR(4),YEAR(GETDATE()) +'\'
+ CONVERT(VARCHAR(25), #MAXID + ROW_NUMBER() OVER (ORDER BY NAME)) AS APPLICATION_NO
FROM TABLE2

How To Create PK As I need My Code Attached SQL Server 2008?

I am trying to create PK as CP-00001 but my code is creating PK as INV-19072013-0001 in my stored procedure I need to create like CP-00001 if no row exist other wise it increment in previous id and then id become CP-00002
This is my stored procedure:
ALTER procedure [dbo].[sp_AutoGenerateInoviceNumber]
AS
Declare #InvNumberDate varchar(15)
Select #InvNumberDate = REPLACE(CONVERT(VARCHAR(10), GETDATE(), 105),'-','')
Declare #NewNumber varchar(20)
If Exists (Select 1 from tbl_InvoiceMaster Where InvoiceNo LIKE '%'+ #InvNumberDate +'%')
Begin
Select Top 1 #NewNumber = InvoiceNo
from tbl_InvoiceMaster
Where InvoiceNo LIKE '%'+ #InvNumberDate +'%'
order by ROW_ID desc
Declare #NewSerialNo varchar(10)
Set #NewSerialNo = SUBSTRING(#NewNumber, CHARINDEX('-',#NewNumber, 5) + 1, LEN(#NewNumber))
Set #NewNumber = SUBSTRING(#NewNumber, 0, CHARINDEX('-',#NewNumber, 5))
Select #NewNumber = #NewNumber + '-' + dbo.func_IncrementNumber(#NewSerialNo)
End
Else
Set #NewNumber = 'INV-'+#InvNumberDate+'-0001'
Select #NewNumber
Hopes for your suggestion thanks
Use the identity property on you PK and add the business logic for "CP-" in a view.
More details on indentiy can be found here

Auto increment Alphanumeric ID in MSSQL

I have an existing Stored procedure that generate employee ID. The employee ID have a format of EPXXXX, EP then 4 numeric values. I want my stored procedure to be shorten.
given the table (tblEmployee) above. Below is the stored procedure for inserting the new employee with the new employee number. The process is I have to get the last employee id, get the last 4 digits (which is the number), convert it to integer, add 1 to increment, check if the number is less than 10, 100 or 1000 or equal/greater than 1000, add the prefix before inserting the new records to the table.
create procedure NewEmployee
#EmployeeName VARCHAR(50)
AS
BEGIN
SET NOCOUNT ON
DECLARE #lastEmpID as VARCHAR(6)
SET #lastEmpID =
(
SELECT TOP 1 Employee_ID
FROM tblEmployee
ORDER BY Employee_ID DESC
)
DECLARE #empID as VARCHAR(4)
SET #empID =
(
SELECT RIGHT(#lastEmpID, 4)
)
DECLARE #numEmpID as INT
#numEmpID =
(
SELECT CONVERT(INT, #empID) + 1
)
DECLARE #NewEmployeeID as VARCHAR(6)
IF #numEmp < 10
SET #NewEmployee = SELECT 'EP000' + CONVERT(#EmpID)
IF #numEmp < 100
SET #NewEmployee = SELECT 'EP00' + CONVERT(#EmpID)
IF #numEmp < 1000
SET #NewEmployee = SELECT 'EP0' + CONVERT(#EmpID)
IF #numEmp >= 1000
SET #NewEmployee = SELECT 'EP' + CONVERT(#EmpID)
INSERT INTO tblEmployee(Employee_ID, Name)
VALUES (#NewEmployeeID, #EmployeeName)
END
Try this one -
CREATE PROCEDURE dbo.NewEmployee
#EmployeeName VARCHAR(50)
AS BEGIN
SET NOCOUNT ON;
INSERT INTO dbo.tblEmployee(Employee_ID, Name)
SELECT
'EP' + RIGHT('0000' + CAST(Employee_ID + 1 AS VARCHAR(4)), 4)
, #EmployeeName
FROM (
SELECT TOP 1 Employee_ID = CAST(RIGHT(Employee_ID, 4) AS INT)
FROM dbo.tblEmployee
ORDER BY Employee_ID DESC
) t
END
I'm not suggesting over what you have currently but, i'd do this way. This is the way I've implemented in my application. Which im gonna give you. Hope you Like this. This is fully Dynamic and Works for all the Transaction you could have.
I've a table Which hold the Document Number as :
CREATE TABLE INV_DOC_FORMAT(
DOC_CODE VARCHAR(10),
DOC_NAME VARCHAR(100),
PREFIX VARCHAR(10),
SUFFIX VARCHAR(10),
[LENGTH] INT,
[CURRENT] INT
)
Which would hold the Data Like :
INSERT INTO INV_DOC_FORMAT(DOC_CODE,DOC_NAME,PREFIX,SUFFIX,[LENGTH],[CURRENT])
VALUES('01','INV_UNIT','U','',5,0)
INSERT INTO INV_DOC_FORMAT(DOC_CODE,DOC_NAME,PREFIX,SUFFIX,[LENGTH],[CURRENT])
VALUES('02','INV_UNIT_GROUP','UG','',5,0)
And, i'd have a fUNCTION OR Procedure but, i've an function here Which would generate the Document Number.
CREATE FUNCTION GET_DOC_FORMAT(#DOC_CODE VARCHAR(100))RETURNS VARCHAR(100)
AS
BEGIN
DECLARE #PRE VARCHAR(10)
DECLARE #SUF VARCHAR(10)
DECLARE #LENTH INT
DECLARE #CURRENT INT
DECLARE #FORMAT VARCHAR(100)
DECLARE #REPEAT VARCHAR(10)
IF NOT EXISTS(SELECT DOC_CODE FROM INV_DOC_FORMAT WHERE DOC_CODE=#DOC_CODE)
RETURN ''
SELECT #PRE= PREFIX FROM INV_DOC_FORMAT WHERE DOC_CODE=#DOC_CODE
SELECT #SUF= SUFFIX FROM INV_DOC_FORMAT WHERE DOC_CODE=#DOC_CODE
SELECT #LENTH= [LENGTH] FROM INV_DOC_FORMAT WHERE DOC_CODE=#DOC_CODE
SELECT #CURRENT= [CURRENT] FROM INV_DOC_FORMAT WHERE DOC_CODE=#DOC_CODE
SET #REPEAT=REPLICATE('0',(#LENTH-LEN(CONVERT(VARCHAR, #CURRENT))))
SET #FORMAT=#PRE + #REPEAT +CONVERT(VARCHAR, #CURRENT+1) + #SUF
RETURN #FORMAT
END
You can use the Function like :
INSERT INTO INV_UNIT(UNIT_CODE,UNIT_NAME,UNIT_ALIAS,APPROVED,APPROVED_USER_ID,APPROVED_DATE)
VALUES(DBO.GET_DOC_FORMAT('01'),#Unit_Name,#Unit_Alias,#APPROVED,#APPROVED_USER_ID,#APPROVED_DATE)
--After Transaction Successfully complete, You can
UPDATE INV_DOC_FORMAT SET [CURRENT]=[CURRENT]+1 WHERE DOC_CODE='01'
Or, you can create an Single Procedure which would handle all the things alone too.
Hope you got the way...
Hence,
Looking at your Way, you are making an Mistake.
You are getting
SET #lastEmpID =
(
SELECT TOP 1 Employee_ID
FROM tblEmployee
ORDER BY Employee_ID DESC
)
Last employee id, and then you are manipulating the rest of the ID. This would create or reuse the ID that was generated earlier however deleted now.
Suppose EMP0010 was there. After some day that EMP has been Deleted. So, When you again create an Employeee next time, You gonna have Same Emp ID you had before for anohter Employe but no more exits however. I dont think thats a good idea.
And, Instead of this :
DECLARE #NewEmployeeID as VARCHAR(6)
IF #numEmp < 10
SET #NewEmployee = SELECT 'EP000' + CONVERT(#EmpID)
IF #numEmp < 100
SET #NewEmployee = SELECT 'EP00' + CONVERT(#EmpID)
IF #numEmp < 1000
SET #NewEmployee = SELECT 'EP0' + CONVERT(#EmpID)
IF #numEmp >= 1000
SET #NewEmployee = SELECT 'EP' + CONVERT(#EmpID)
Which you used to repeat an Zero. You would use Replicate Function() of SQL. Like above on the Example of Mine.
SET #REPEAT=REPLICATE('0',(#LENTH-LEN(CONVERT(VARCHAR, #CURRENT))))
I don't think you need a Stored Procedure , Try using Ranking Functions
select
'EP'+RIGHT('000000'+ CAST(ROW_NUMBER() OVER (ORDER BY Name) AS VARCHAR(6)), 4)
AS [emp_code]
,
Name
FROM emp1 WITH(NOLOCK)
SQL Fiddle
EDIT
select
'EP'+RIGHT('000000'+ CAST((ROW_NUMBER() OVER (ORDER BY Name)+10) AS VARCHAR(6)), 4)
AS [emp_code] --^Add the last Emp no.
,
Name
FROM emp1 WITH(NOLOCK)
SQL Fiddle
of course the accepted answer is working fine, but it is not working if we have numm in previous values. so modified it as below, hope this will help others as well
CREATE PROCEDURE dbo.NewEmployee
#EmployeeName VARCHAR(50)
AS BEGIN
SET NOCOUNT ON;
INSERT INTO dbo.tblEmployee(Employee_ID, Name)
SELECT
'EP' + RIGHT('0000' + CAST(Employee_ID + 1 AS VARCHAR(4)), 4)
, #EmployeeName
FROM (
SELECT TOP 1 Employee_ID = CAST(RIGHT(Employee_ID, 4) AS INT)
FROM dbo.tblEmployee
ORDER BY Employee_ID DESC
) t
END