Running Out of INT
The Problem
Let's take a moment here just in case someone reading is unfamiliar. In many systems I've worked with, a table will have a Primary Key that is an INT (integer) data type. Most frequently an ID field. Not something you'd necessarily query for or put into a report, but it's a means of identifying a record and helping to speed up searches, an effective column to join other tables on, perhaps a foreign key here and there too. Ninety Nine percent of time, it's a good bet that the field is setup with the Identity (1,1) configuration too. Meaning the records inserted to the table will start at a value of 1 and increment by 1.
The INT data type has a maximum value of 2,147,483,647. Now at first glance, that feels like a huge, never achievable number. Who wouldn't love to have 2 billion orders? However, if that table has many years of history or even better is keeping an event log, that could definitely get hit. Per my opening paragraph example, you don't want to get caught napping when that next record attempts to be inserted.
Let me lay out an example I encountered a couple of years ago. I work with an application that was doing this very thing. There was an event log table with an ID column set as the INT datatype and it was growing rapidly. This app was developed many years ago with little thought as to how much it would grow and be used. One day, it suddenly stopped logging and throwing all manner of errors. We discovered the event table was maxed out.
The Solutions?
You might say, oh just change the data type to BIGINT (a maximum value of 9,223,372,036,854,775,807 by the way). Sure, sure....do you know what happens when you try to change a data type on the fly? SQL Server has to go in row by row and convert that value and write it back to the table. Any idea how long that would take or what it would do to your TempDB, memory or indexes? No. Thank. You.
Purge old rows and reseed the Identity? Alright, and those other tables that reference THIS ID field? Orphan records or even worse, next time you query you get duplicate results since you'll have used the same ID value again. Heaven help you if you have Foreign Keys on that column. Unless you turn off the check constraints, the engine then has to go verify that the referenced value is also gone. Guess how it does that? Row by agonizing row (RBAR for short). Nope, that's out too.
In our case, the super quick fix to get the application back online and operational was to change the Identity setting to (-2,147,483,647,1). That's the maximum negative value you can set. Since a negative number is still an integer. It would start there and increment by 1 again. That is until you reach 1. Then you're really stuck. However, do you like seeing negative values in your data? Even if it is just an ID field? I do not. Nevertheless, we needed the app back online and quickly so the fix was in.
Our End Solution
After all of the records were moved out, the _OLD table was dropped, the view was dropped and the _BIGINT table renamed to simply OrigTableName. Bada bing, we're back in business.
Is This Still An Issue?
I Want to Help You!
I wrote this blog today with the notion that maybe someone will read it who's using older legacy systems and take note to go have a look at their databases. See if any of those INT data types exist. I certainly do not want any of your production systems to hit this snag at a critical moment. Or overnight. I wouldn't want a phone call at 2 a.m. and with my sleep deprived brain try to finagle a solution.Most of what I find these days are nowhere close to hitting the max, but it's peace of mind to have that data.
Here is a link to the script: GitHub
I'm sure you intrepid DBAs will find a way to tweak and make your own. If you have questions about it, hit me up! I love working with stuff like this.

This comment has been removed by the author.
ReplyDeleteI've ran into this same problem with a few tables. From a database point of view the above solution might work but it forgets to mention the "new" view with the union will not take inserts/updates/deletes AS-IS. An INSTEAD OF trigger would be needed on the view. The application might need a new build too, changing the original ID field from an INT to a BIGINT in the database does not change the application ID variable to a BIGINT if that is used in the application. The app might crash on that bigger value.
ReplyDeleteValid points. In our particular example, this was occurring really only this table. There's definitely much to think about and consider when switching data types. I suppose the main point I was making was looking out for the potential running out of values problem so it could be addressed before it becomes critical.
DeleteWhen the ID column was in an referenced table (dbo.orders), it doesn't help just to change it in the main table, each occurence / reference in other tables (order_history, order_details, order_positions...) needs to be changed too. And of course there may be tons of procedures that are using that ID and have declared variables for this purpose.
ReplyDeleteAnother problem may occure, when you are used to create a new order by something as:
EXEC @new_order_id = dbo.usp_create_new_order
since the result value of an procedure is always an INT.
In that case you would need to define an OUTPUT parameter and change every procedure call to
EXEC dbo.usp_create_new_order , @new_order_id = @new_order_id OUTPUT
Excellent input! Certainly wasn't meant to be an end all be all solution. Merely pointing out it's a gotchas that may arise when using the INT data type and super quick and dirty solution that we employed.
DeleteCouldn't I just create a duplicate table with the BIGINT identity, insert all existing records over, and then rename the new table to the old one?
ReplyDeleteOf course, and that's basically what they did here. But the problem is how long that takes and if the database is allowed to be offline that long. If you're already approaching the max of INT, it takes a very long time. This article says the copying to the new table took 2 weeks running 6 hours each night - in other words, 84 hours, or 3.5 days. If you can't keep the database offline that long, then you need to jump through these hoops.
Delete