Generate sequence number in SQL on every insert - sql

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.

Related

SQL Server - Looking for a way to shorten code

I'm basically very new to SQL Server, so please bare with me. Here is my problem:
I have a table with (let's say) 10 columns and 80k rows. I have 1 column called Date in the format of YYYY-MM-DD type varchar(50) (can't convert it to date or datetime type I tried, the initial source of data is not good).
**Example :
Table [dbo].[TestDates]
Code
SellDate
XS4158
2019-11-26
DE7845
2020-02-06
What I need to do is to turn the YYYY-MM-DD format to DD/MM/YYYY format. After a lot of tries (I tried the functions (DATE_FORMAT, CONVERT, TO_DATE etc) and this is solution :
1- I added a primary key for join purpose later (ID)
2- I split my date column in 3 columns in a whole new table
3- I merged the 3 columns in the order I need with the delimiter of my choice (/) in the same new table
4- I copied the good column to my initial table using the primary key ID I created before
alter table [dbo].[TestDates]
add ID int not null IDENTITY primary key;
SELECT ID,
FORMAT(DATEPART(month, [SellDate]),'00') AS Month,
FORMAT(DATEPART(day, [SellDate]),'00') AS Day,
FORMAT(DATEPART(year, [SellDate]),'0000') AS Year
INTO [dbo].[TestDates_SPLIT]
FROM [dbo].[TestDates]
GO
ALTER TABLE [dbo].[TestDates_SPLIT]
ADD SellDate_OK varchar(50)
UPDATE [dbo].[TestDates_SPLIT]
SET SellDate_OK = [Day] + '/' + [Month] + '/' + [Year]
ALTER TABLE [dbo].[TestDates_SPLIT]
DROP COLUMN Month, Day, Year
ALTER TABLE [dbo].[TestDates]
ADD SellDate_GOOD varchar(50)
UPDATE [dbo].[TestDates]
SET [TestDates].SellDate_GOOD = [TestDates_SPLIT].SellDate_OK
FROM [dbo].[TestDates]
INNER JOIN [dbo].[TestDates_SPLIT]
ON [TestDates].ID = [TestDates_SPLIT].ID
This code works but i find too heavy and long, considering I have 6 more dates columns to work on. Is there a way to make it shorter or more efficient? Maybe with SET SellDate = SELECT (some query of sorts that doesn't require to create and delete table)
Thank you for your help
I tried the usual SQL functions but since my column is a varchar type, the converting was impossible
You should not be storing dates as text. But, that being said, we can try doing a rountrip conversion from text YYYY-MM-DD to date to text DD/MM/YYYY:
WITH cte AS (
SELECT '2022-11-08' AS dt
)
SELECT dt, -- 2022-11-08
CONVERT(varchar(10), CONVERT(datetime, dt, 121), 103) -- 08/11/2022
FROM cte;
Demo

SQL Server - Reset custom identity column

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

Auto increment primary key and reseed every month

What is the best solution to create SQL table which has a composite primary key.
First column of the primary key is ID {int}.
Second column of the primary key is YearMonth {string like yyyyMM ex. 201409 } which is current month.
that can reseed an ID to 1 every month
And I actually need the running ID like
"201409-00001"
"201409-00002"
"201409-00003"
.
.
"201410-00001"
"201410-00002"
.
.
"201411-00001"
Based on the question and your comments, it seems that you need to display the a row identifier in the format 201411-00001. This does not need to be your actual primary key. What I would recommend is that you do something like this.
create your table with ID which is Identity. Add another column YearMonth CHAR(6) column which stores YYYYMM or if you already have a column which stores the date as date/datetime, use that. In your SELECTs you would do something like this
LEFT(CONVERT(VARCHAR(10),YearMonth,112),6) + '-' + RIGHT(REPLICATE('0',5) + CONVERT(VARCHAR(5),ROW_NUMBER() OVER(PARTITION BY DateCol, ORDER BY ID),5)
You would alternately have a trigger which updates a new INT column monthlyID based on the MAX(monthlyID) + 1 for the month.
Create sequence start at 1 and concat it to your 'year,month'
Create job at every 1st of month and restart the sequence
Did you try concatenating them and creating a new column to make it the primary key?
Don't know if there is a simpler method,but this should work:
SELECT CONCAT(YEAR(CURRENT_TIMESTAMP), MONTH(CURRENT_TIMESTAMP),'-',ID)
FROM <TABLE NAME>;
Finally, I try using the concept of Mr. #ughai
and I using the query like below to keep the values on another column for represent the ID with month relatively.
select LEFT(CONVERT(VARCHAR(10),YearMonth,112),6) + '-' + RIGHT(REPLICATE('0',4) + CONVERT(VARCHAR(5),ROW_NUMBER() OVER(PARTITION BY LEFT(CONVERT(VARCHAR(10),YearMonth,112),6) ORDER BY ID)),5) from TestTable
Try something like this:
ALTER TABLE table_name AUTO_INCREMENT = 1
and this:
ALTER TABLE foobar_data MODIFY COLUMN col VARCHAR(255) NOT NULL DEFAULT '201409';
and run this query via some method every month ;)

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.

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.