Periodic restarting auto numbering for SQL Server - sql-server-2005

I have a requirement for a program I'm working on to store job numbers as YY-###### with incrementing numbers starting at 000001 in each year preceded by the last two digits of the year.
The only method I've been able to come up with is to make a CurrentJob table and use an identity column along with the last two digits of the year and an ArchiveJob table and then combining the two via a union in a view. Then I'd have to copy the CurrentJob to ArchiveJob at the begining of the year and truncate CurrentJob.
Is there an easier way to restart the numbering (obviously not having it be an Identity column) in one table?
The client is closed on New Years so there should be no data entry at the change of the year (for a school).

An identity column is by far the fastest and most concurrent solution to generating sequential numbers inside SQL Server. There's no need to make it too complicated though. Just have one table for generating the identity values, and reset it at the end of the year. Here's a quick example:
-- Sequence generating table
create table SequenceGenerator (
ID integer identity(1, 1) primary key clustered
)
-- Generate a new number
insert into SequenceGenerator default values
select ##identity
-- Reset the sequence
truncate table SequenceGenerator
if ident_current('SequenceGenerator') <> 1 begin
dbcc checkident('SequenceGenerator', reseed, 0)
dbcc checkident('SequenceGenerator', reseed)
end else begin
dbcc checkident('SequenceGenerator', reseed, 1)
end

There is a similar question #761378.. (Note: it uses MySql but the principle is the same)
The accepted answer suggested using a second table to manage the current ID.
However the most popular question was to not do this! Please note HLGEM's answer on the post for reasons why not to.

You can use the "reseed" command from here to reset the starting value.

Related

GORM Auto-increments primary key even if data wasnt inserted into DB [duplicate]

I'm using MySQL's AUTO_INCREMENT field and InnoDB to support transactions. I noticed when I rollback the transaction, the AUTO_INCREMENT field is not rollbacked? I found out that it was designed this way but are there any workarounds to this?
It can't work that way. Consider:
program one, you open a transaction and insert into a table FOO which has an autoinc primary key (arbitrarily, we say it gets 557 for its key value).
Program two starts, it opens a transaction and inserts into table FOO getting 558.
Program two inserts into table BAR which has a column which is a foreign key to FOO. So now the 558 is located in both FOO and BAR.
Program two now commits.
Program three starts and generates a report from table FOO. The 558 record is printed.
After that, program one rolls back.
How does the database reclaim the 557 value? Does it go into FOO and decrement all the other primary keys greater than 557? How does it fix BAR? How does it erase the 558 printed on the report program three output?
Oracle's sequence numbers are also independent of transactions for the same reason.
If you can solve this problem in constant time, I'm sure you can make a lot of money in the database field.
Now, if you have a requirement that your auto increment field never have gaps (for auditing purposes, say). Then you cannot rollback your transactions. Instead you need to have a status flag on your records. On first insert, the record's status is "Incomplete" then you start the transaction, do your work and update the status to "compete" (or whatever you need). Then when you commit, the record is live. If the transaction rollsback, the incomplete record is still there for auditing. This will cause you many other headaches but is one way to deal with audit trails.
Let me point out something very important:
You should never depend on the numeric features of autogenerated keys.
That is, other than comparing them for equality (=) or unequality (<>), you should not do anything else. No relational operators (<, >), no sorting by indexes, etc. If you need to sort by "date added", have a "date added" column.
Treat them as apples and oranges: Does it make sense to ask if an apple is the same as an orange? Yes. Does it make sense to ask if an apple is larger than an orange? No. (Actually, it does, but you get my point.)
If you stick to this rule, gaps in the continuity of autogenerated indexes will not cause problems.
I had a client needed the ID to rollback on a table of invoices, where the order must be consecutive
My solution in MySQL was to remove the AUTO-INCREMENT and pull the latest Id from the table, add one (+1) and then insert it manually.
If the table is named "TableA" and the Auto-increment column is "Id"
INSERT INTO TableA (Id, Col2, Col3, Col4, ...)
VALUES (
(SELECT Id FROM TableA t ORDER BY t.Id DESC LIMIT 1)+1,
Col2_Val, Col3_Val, Col4_Val, ...)
Why do you care if it is rolled back? AUTO_INCREMENT key fields are not supposed to have any meaning so you really shouldn't care what value is used.
If you have information you're trying to preserve, perhaps another non-key column is needed.
I do not know of any way to do that. According to the MySQL Documentation, this is expected behavior and will happen with all innodb_autoinc_lock_mode lock modes. The specific text is:
In all lock modes (0, 1, and 2), if a
transaction that generated
auto-increment values rolls back,
those auto-increment values are
“lost.” Once a value is generated for
an auto-increment column, it cannot be
rolled back, whether or not the
“INSERT-like” statement is completed,
and whether or not the containing
transaction is rolled back. Such lost
values are not reused. Thus, there may
be gaps in the values stored in an
AUTO_INCREMENT column of a table.
If you set auto_increment to 1 after a rollback or deletion, on the next insert, MySQL will see that 1 is already used and will instead get the MAX() value and add 1 to it.
This will ensure that if the row with the last value is deleted (or the insert is rolled back), it will be reused.
To set the auto_increment to 1, do something like this:
ALTER TABLE tbl auto_increment = 1
This is not as efficient as simply continuing on with the next number because MAX() can be expensive, but if you delete/rollback infrequently and are obsessed with reusing the highest value, then this is a realistic approach.
Be aware that this does not prevent gaps from records deleted in the middle or if another insert should occur prior to you setting auto_increment back to 1.
INSERT INTO prueba(id)
VALUES (
(SELECT IFNULL( MAX( id ) , 0 )+1 FROM prueba target))
If the table doesn't contain values or zero rows
add target for error mysql type update FROM on SELECT
If you need to have the ids assigned in numerical order with no gaps, then you can't use an autoincrement column. You'll need to define a standard integer column and use a stored procedure that calculates the next number in the insert sequence and inserts the record within a transaction. If the insert fails, then the next time the procedure is called it will recalculate the next id.
Having said that, it is a bad idea to rely on ids being in some particular order with no gaps. If you need to preserve ordering, you should probably timestamp the row on insert (and potentially on update).
Concrete answer to this specific dilemma (which I also had) is the following:
1) Create a table that holds different counters for different documents (invoices, receipts, RMA's, etc..); Insert a record for each of your documents and add the initial counter to 0.
2) Before creating a new document, do the following (for invoices, for example):
UPDATE document_counters SET counter = LAST_INSERT_ID(counter + 1) where type = 'invoice'
3) Get the last value that you just updated to, like so:
SELECT LAST_INSERT_ID()
or just use your PHP (or whatever) mysql_insert_id() function to get the same thing
4) Insert your new record along with the primary ID that you just got back from the DB. This will override the current auto increment index, and make sure you have no ID gaps between you records.
This whole thing needs to be wrapped inside a transaction, of course. The beauty of this method is that, when you rollback a transaction, your UPDATE statement from Step 2 will be rolled back, and the counter will not change anymore. Other concurrent transactions will block until the first transaction is either committed or rolled back so they will not have access to either the old counter OR a new one, until all other transactions are finished first.
SOLUTION:
Let's use 'tbl_test' as an example table, and suppose the field 'Id' has AUTO_INCREMENT attribute
CREATE TABLE tbl_test (
Id int NOT NULL AUTO_INCREMENT ,
Name varchar(255) NULL ,
PRIMARY KEY (`Id`)
)
;
Let's suppose that table has houndred or thousand rows already inserted and you don't want to use AUTO_INCREMENT anymore; because when you rollback a transaction the field 'Id' is always adding +1 to AUTO_INCREMENT value.
So to avoid that you might make this:
Let's remove AUTO_INCREMENT value from column 'Id' (this won't delete your inserted rows):
ALTER TABLE tbl_test MODIFY COLUMN Id int(11) NOT NULL FIRST;
Finally, we create a BEFORE INSERT Trigger to generate an 'Id' value automatically. But using this way won't affect your Id value even if you rollback any transaction.
CREATE TRIGGER trg_tbl_test_1
BEFORE INSERT ON tbl_test
FOR EACH ROW
BEGIN
SET NEW.Id= COALESCE((SELECT MAX(Id) FROM tbl_test),0) + 1;
END;
That's it! You're done!
You're welcome.
$masterConn = mysql_connect("localhost", "root", '');
mysql_select_db("sample", $masterConn);
for($i=1; $i<=10; $i++) {
mysql_query("START TRANSACTION",$masterConn);
$qry_insert = "INSERT INTO `customer` (id, `a`, `b`) VALUES (NULL, '$i', 'a')";
mysql_query($qry_insert,$masterConn);
if($i%2==1) mysql_query("COMMIT",$masterConn);
else mysql_query("ROLLBACK",$masterConn);
mysql_query("ALTER TABLE customer auto_increment = 1",$masterConn);
}
echo "Done";

non-identity column auto increment

I'm working on a project to consolidate data (from 2 different DBs). I have created a table that contains a few columns:
MAPPING_ID int
ContentID int
ContentValue varchar(200)
For Example, when I do my 1st set of inserts against the original data source everything is good.
Mapping_ID: 53
ContentID: 53
ContentValue: Original Data 1
Mapping_ID: 54
ContentID: 54
ContentValue: Original Data 2
But when I do my second set of inserts against the another source (the data I'm trying to merge) I would like the Mapping_ID column to continue to the next number (i.e. 55,56,57...)
I looked at the row_number function but that starts at 1. Is there a way to start it at 55?
I suppose I could make that Mapping_ID column an Identity field, but turn it off during the first insert and then seed it with the max value (54) and then turn it on during the second insert.
Is there another way to accomplish this?
In SQL Server 2012+, you may use SEQUENCE objects to populate non-identity columns with autoincrement values. Plus you may use same SEQUENCE for different tables to make numeration pass-through and obtain values from sequences in SELECT and UPDATE queries.
First, create SEQUENCE:
CREATE SEQUENCE SchemaName.SequenceName
START WITH 1
INCREMENT BY 1 ;
Then, create DEFAULT constraint with values from sequence on required column:
ALTER TABLE tableName ADD CONSTRAINT constraint_unique_name DEFAULT NEXT VALUE FOR SchemaName.SequenceName FOR Mapping_ID;
There's actually a brand new way to do this kind of thing as of SQL Server 2012: the sequence object. I'm sorry I can't script out a procedure for you as I'm working in the MySQL world at present, but it's super easy to implement. The basic idea is you're maintaining a separate database object with its own seed and increment amount, but there are some caveats to bear in mind regarding their difference from traditional identity values (e.g. you can overwrite them), so make sure you do some research.
Here are a couple of articles to get you started. If you have trouble, hit me back and I'll try to work through the code with you.
https://msdn.microsoft.com/en-us/library/ff878091.aspx
https://msdn.microsoft.com/en-us/library/ff878370.aspx
Good luck!
To get row_number() to start at 55, you could just add 54 (or whatever number) to your row_number() calculation:
(row_number() over (partition by Y order by X)) + 54

How to create a smart key, which would reset its auto increment value at the end of the month

I am currently working on a project for the management of oil distribution, and i need the receipts of every bill to get stored in a database. I am thinking of building a smart key for the receipts which will contain the first 2 letters of the city, the gas station id, the auto increment number, first letter of the month and the last 2 digits of the year. So it will be somewhat like this:
"AA-3-0001-J15". What i am wondering is how to make the AI number to go back at 0001 when the month changes. Any suggestions?
To answer the direct question - how to make the number restart at 1 at the beginning of the month.
Since it is not a simple IDENTITY column, you'll have to implement this functionality yourself.
To generate such complex value you'll have to write a user-defined function or a stored procedure. Each time you need a new value of your key to insert a new row in the table you'll call this function or execute this stored procedure.
Inside the function/stored procedure you have to make sure that it works correctly when two different sessions are trying to insert the row at the same time. One possible way to do it is to use sp_getapplock.
You didn't clarify whether the "auto increment" number is the single sequence across all cities and gas stations, or whether each city and gas station has its own sequence of numbers. Let's assume that we want to have a single sequence of numbers for all cities and gas stations within the same month. When month changes, the sequence restarts.
The procedure should be able to answer the following question when you run it: Is the row that I'm trying to insert the first row of the current month? If the generated value is the first for the current month, then the counter should be reset to 1.
One method to answer this question is to have a helper table, which would have one row for each month. One column - date, second column - last number of the sequence. Once you have such helper table your stored procedure would check: what is the current month? what is the last number generated for this month? If such number exists in the helper table, increment it in the helper table and use it to compose the key. If such number doesn't exist in the helper table, insert 1 into it and use it to compose the key.
Finally, I would not recommend to make this composite value as a primary key of the table. It is very unlikely that user requirement says "make the primary key of your table like this". It is up to you how you handle it internally, as long as accountant can see this magic set of letters and numbers next to the transaction in his report and user interface. Accountant doesn't know what a "primary key" is, but you do. And you know how to join few tables of cities, gas stations, etc. together to get the information you need from a normalized database.
Oh, by the way, sooner or later you will have more than 9999 transactions per month.
Do you want to store all that in one column? That sounds to me like a composite key over four columns...
Which could look like the following:
CREATE TABLE receipts (
CityCode VARCHAR2(2),
GasStationId NUMERIC,
AutoKey NUMERIC,
MonthCode VARCHAR2(2),
PRIMARY KEY (CityCode, GasStationId, AutoKey, MonthCode)
);
Which DBMS are you using? (MySQL, MSSQL, PostgreSQL, ...?)
If it's MySQL you could have a batch-job which runs on the month's first which executes:
ALTER TABLE tablename AUTO_INCREMENT = 1
But that logic would be on application layer instead of DB-layer...
In such cases, it is best to use a User-Defined function to generate this key and then store it. Like :
Create Function MyKeyGenerator(
#city varchar(250) = '',
#gas_station_id varchar(250) = '')
AS
/*Do stuff here
*/
My guess is , you may need another little table that keeps the last generated auto-number for the month and you may need to update it for the first record that generates during the month. For the next records, during the month, you will fetch from there and increment by 1. You can alse use a stored procedure that returns an Integer as a return code, just for the autonumber part and then do the rest in a function.
Btw, you may want to note that, using the first letter of the month has pitfalls, because two months can have the same first letter. May be try the the two-digit-numeric for the month or the first three letters of the month name.
If you ready not to insist the the AI number exactly be of identity type, you can have another table, where it is a non-identity regular integer, and then run an SQL Server Agent Task calling a stored procedure that'll do the incrementing business.

Identities Appearing in Tables have Gaps [duplicate]

This question already has answers here:
Identity increment is jumping in SQL Server database
(6 answers)
Closed 9 years ago.
We have begun to see identities created in some of our tables are no longer precisely sequential. That is to say they remain incrementally higher but there are large gaps in the values.
For instance, the sequence is {1,2,3.. .. 97,98,99} and then a jump to {1092,1093,1094.. .. 1097,1098,1099} followed by another gap and then {4231,4232,4233.. .. 4257,4258,4259}.
Can anyone shed any light on this behaviour?
If you execute an insert to a table that has an identity column, and the insert fails (for ANY reason), the identity value is still incremented and the next insert will leave a gap. Also, if you delete rows there will obviously be gaps.
NEVER rely on, or use, the actual value of a surrogate key, or identity for anything other than as a "connection value" to connect row or rows in one table to rows in another table. Certainly never rely on the sequence of values being contiguous, nor even that they are chronological increasing.
This is a known bug. Your large gaps are caused by things like failover, service restart, reboots, etc.
http://connect.microsoft.com/SQLServer/feedback/details/739013/failover-or-restart-results-in-reseed-of-identity
Until it is fixed, there's not much you can do about it except maybe have a startup procedure that reseeds the identity column on all affected tables.
Anytime you insert into a table with an identity column it increments the identity. If you delete the rows, or even if you rollback an insert into that table the identity column stays at the new increment.
Here's a script to show the effect of rolling back a transaction:
create table #temp (TheKey int identity(1,1), TheValue int)
insert into #Temp (TheValue) values (1)
select max(TheKey) from #Temp --1 as expected
begin tran
insert into #Temp (TheValue) values (1)
select max(TheKey) from #Temp --2 as expected
rollback
select max(TheKey) from #Temp --1 as expected
insert into #Temp (TheValue) values (1)
select max(TheKey) from #Temp --3 a little bit of a surprise?
It appears that the server caches values for performance and so if the server is reset after a power outage for instance then those values can be lost. See this article.
Consecutive values after server restart or other failures

Auto Increment feature of SQL Server

I have created a table named as ABC. It has three columns which are as follows:-
The column number_pk (int) is the primary key of my table in which I have made the auto increment feature on for that column.
Now I have deleted two rows from that table say Number_pk= 5 and Number_pk =6.
The table which I get now is like this:-
Now if I again enter two new rows in this table with the same value I get the two new Number_pk starting from 7 and 8 i.e,
My question is that what is the logic behind this since I have deleted the two rows from the table. I know that a simple answer is because I have set the auto increment on for the primary key of my table. But I want to know is there any way that I can insert the two new entries starting from the last Number_pk without changing the design of my table?
And how the SQL Server manage this record since I have deleted the rows from the database??
The logic is guaranteeing that the generated numbers are unique. An ID field does not neccessarily have to have a meaning, but rather is most often used to identify a unique record, thus making it easier to perform operations on it.
If your database is designed properly, the deleted ID numbers would not have been possible to delete if they were referenced by any other tables in a foreign key relationship, thus preventing records from being orphaned in that way.
If you absolutely want to have entries sequences, you could consider issuing a RESEED, but as suggested, it would not really give you much advantages.
The identity record is "managed" because SQL Server will keep track of which numbers have been issued, regardless of whether they are still present or not.
Should you ever want to delete all records from a table, there are two ways to do so (provided no foreign key relatsons exist):
DELETE FROM Table
DELETE just removes the records, but the next INSERTED value will continue where the ID numbering left of.
TRUNCATE TABLE
TRUNCATE will actually RESEED the table, thus guaranteeing it starts again at the value you originally specified (most likely 1).
Although you should not do this until their is a specific requirement.
1.) Get the max id:
Declare #id int
Select #id = Max(Number_pk) From ABC
SET #id = #id + 1;
2.) And reset the Identity Column:
DBCC CHECKIDENT('ABC', RESEED, #id)
DBCC CHECKIDENT (Transact-SQL)