- Feature Articles
- CodeSOD
- Error'd
- Forums
-
Other Articles
- Random Article
- Other Series
- Alex's Soapbox
- Announcements
- Best of…
- Best of Email
- Best of the Sidebar
- Bring Your Own Code
- Coded Smorgasbord
- Mandatory Fun Day
- Off Topic
- Representative Line
- News Roundup
- Editor's Soapbox
- Software on the Rocks
- Souvenir Potpourri
- Sponsor Post
- Tales from the Interview
- The Daily WTF: Live
- Virtudyne
Admin
Why would that be a WTF? Perl is the A-1 choice for WTF programming worldwide.[;)]
Admin
Anonymous ... beware! There's a lot of bad advice in this thread. There is exactly one optimal way to deal with Identity (sequence) in MS SQL 2000 and later:
Insert into your table
select scope_identity()
Do not use max(), do not use @@identity, and look in BOL so you don't copy some incorrect syntax from the kind folks here.
Admin
No, it froze up everything because it was stuck in an infinite loop, querying the database constantly.
Admin
Run
SELECT @@IDENTITY As InsertedID
after the insert query before closing the connection and the last inserted identity on that connection will be returned
Admin
Again, that's not right.
SCOPE_IDENTITY(). Please. Read up on it.
Admin
I see your point about this being the only possible "put out the fire" fix. The real fix would probably involve creating an identity column > 9 digits (his new scope) and starting the numbering at 1,000,000,000. Identity columns are NUMERIC, so you can use up to 36 digits. The numbers < 10 digits are no great loss.
The one thing I'm not sure of is if you can assign numbers to identity colums (as opposed to letting the DB assign them). I think you can, because BCP works just fine if it contains an IDENTITY column in the BCP file. I'm sure someone will correct me otherwise.
My preferred fix would be GUIDs, though, but the system is already set up using NUMERIC columns, and it'd be even more painful to change that.
Admin
all he should have done is define the global variable LENGTH_OF_ID_THINGY_THING to 9, then replaced the 9's with it, then u only need to update that one value to expand next time!!
Admin
http://www.duk0r.net/matrix/106.jpg
http://www.duk0r.net/matrix/107.jpg
Admin
He also could have started numbering sequentially at 7 digits, and taken at least that much of the WTF out of the PERL code. Thread-safe issues aside (since this isn't anyway), a select max(id) + 1 would have sufficed for the quick-fix. That way he's not randomly dancing all over the table looking for unused numbers. Goes from O(godOnlyKnows) to O(1). Yay!
// waiting for someone named 'god' to fill in the blank. Eesh.
Admin
Btw, is this the first Perl WTF here or did I miss something? I'm waiting for Ruby!
Admin
You don't want to use @@Identity, use SCOPE_IDENTY(). @@Identity will return the last identiy generated during the connection (or could possibly be the last identity the database generated). SCOPE_IDENTITY() only returns the last generated identity value in the current scope.
Admin
The usage alone of ruby in a production project would be a WTF
Admin
Well yeah, but then it wouldn't be random!
Admin
Because it is running in an infinite loop, so every single page access runs forever, not releasing its memory or its thread. Notice that there are two loops - 1) to create the 6 digit number, and 2) one that continues looping until it finds one that hasn't been used. Since they had all been running, it continually generates new numbers and continually queries the database. You get 15 - 20 simultaneous uses of this function and you machine is now (from the users perspective) locked up. The processor is still going, but there are no resources to emit any kind of response.
As an aside, this is quite possibly the worst possible method for creating keys, because as the number of available keys slims down, the time it takes to create one goes up exponentially. I feel really sorry for the users of this system.
Admin
Hugo -- that is a GREAT post! Could not agree more, and very well put.
Thank you, that made my day. :)
Admin
Heh, I thought of that while I was typing, but decided to see if people would catch that, seems like some people did :)
Admin
oh the googles! they do nothing!!.. what a bunch of newbies, this is what WTF is all about!!
the stories are (more often than not) so made up! (exageration of some reality)
Admin
the original question did not demand knowing the identity before inserting. It required returning the inserted identity so it could be used. scope_identity() works just fine for that. The original question was perfectly legit and has a workable solution. As of Sql Server 2000. The real WTF is that it took so long for SqlServer to provide this solution.
Admin
You don't know what you're talking about.
Admin
Hey did anyone mention yet that you can use SCOPE_IDENTITY() ?
It scares me how lately many people seem to think these WTF's are little programming challenges and feel the need to present their "solutions".
Admin
uh ... that's great, thanks for telling us ... not sure what any of that has to do with what you've quoted, but thanks for clearing that up.
Admin
I've solved that problem numerous times with the same solution: I place a record in the system that's marked that it is temporary in nature. Call center applications usually have this requirement. Then when the data is complete, the record is updated with the correct information and the tracking number is also still the PK.
The other approach for developers who have an asshat architect or users that think two round trips will bring down humanity as we know it is to randomly generate this tracking number at the client (like UPS does) with smart-numbering that makes collisions difficult. My personal favorite is using the last 1/2 of the client's subnet in the number. i.e. for machine 192.168.1.54, you could use 168-1-54-CD-0005413 (seperate the number however you want, store it in the DB as a long)... which also works on web apps since the web server knows what IP the client came from, the check digit is calculated from the user's login. The rest of the number can just hash off the time.
Just please don't use MSGUIDs, I hate those, and they make lousy PKs.
Admin
Thanks for clearing that up, and great counter argument.
Admin
The real WTF is this forum sof... oh, wait. Nevermind.
Anyway, I find it mildly interesting that this is actually no different from the technique used to generate the prime numbers for public/private key encryption: you pick 512 (or 1024, or 2048) random bits, then see if it's prime. If not, you pick a new set of bits, then see if it's prime. If it's not, you pick a third set of bits, ...
(of course, no application actually verifies true primality; that would take forever. Google 'Rabin-Miller' for more information.)
Admin
This is what I don't get. What's the benefit of it being random?
And also, how do you have "random order"?
Adam
Admin
I'd agree for the most part... Except, I never tire of declaring Brilliant! when I come across something asinine ~ what can I say, the guiness guys have done well in their advertising campaign
Admin
You know what you described is essentially the GUID algorithm. But yours is much better, I can tell just by reading about it. Especially since you have to implement it yourself. Plus, I wound't want a guarantee of uniqueness like I get with UUID/GUID so I still have to code for collisions anyway. Much better.
If you ever have to write a complex object hieararchy out to the database in a single transaction, you'll realize that it's not just one extra round trip.
Admin
The original programmer could have read each record one a time to verify that the key was unique. That could have made it worse.
Admin
At my last position, we used a DB called Gupta SQLBase. It did not have a sequence/auto-increment/identity field. It sucked bad. The standard practice was to have a table containing the next id number for all tables. We would lock the table, select the value, update it, then unlock and go on our way.
Admin
infinite loop, with a query within each iteration
Admin
No! He is entirely correct. Using *any* language in production in a WTF. Have you not been reading TDWTF for long?
Admin
If he/she had used natural keys he wouldn't have this problem...
go for them Jeff, You had convinced me about using natural keys (really)
Admin
Two uncommited transactions doing the same thing insert will get the same max(id) + 1
Assuming your using a real database that is, not some glorified wrapper around berkely DB files (mysql anyone?)
Admin
Yep - it's one of the two types of local variable declaration in Perl (the other being "local myVariable"). IIRC, My is statically-scoped, Local is dynamically-scoped, and I don't want to even think about how the two interact (fortunately, "local" doesn't seem to be used that much in practice).
Admin
You can create your own sequence table if you must. Then you just start a transaction, select from the table, update the sequence and then commit.
Admin
In that case....I'm gonna write me some WTF's...
Admin
Any system (correctly) implementing try-write is safe. Pseudocode:
sequence number = // highest existing, -1 if none
do
sequence number += 1
// if too big then throw error
try-write // similar to INSERT in SQL; succeeds if and only if record key is not already in the table
until write succeeded
Admin
Nice injection hack
Admin
I actually laughed at this!
Admin
I'm thinking it's unlikely that it was actually reaching the infinite-loop state -- as soon as the keyspace started getting close to full, the loop would have started running many more iterations on average, multiplying the server load by a large factor and bringing the system to its knees, even while the queries were still logically capable of succeeding.
Can you run a million one-row queries before your webserver times out the request? Apache's default timeout is 300 seconds, meaning that to get the last few possible ids, you'd have to be executing the query over 3,000 times per second on average. At a time when there are probably many other requests stuck in the same loop concurrently (because they've all started taking so long to complete, so more of them will be overlapping).
So unless the DB server hardware was ridiculously over-specced for the application's normal workload, the system probably would have started dropping requests before it fully ran out of ids. And a dropped request wouldn't consume one of the few remaining possible ids, so it could run in that state for quite a while. Eventually, a few requests would get lucky enough to find those last few ids, and then it'd reach the infinite-loop state. But the wording "...completely freeze up within minutes..." makes me think that they were just seeing it get close enough for requests to start hanging (before the server(s) crashed?).
Admin
Auto-incremented transaction ID's are just as stupid as creating transaction ID's with such a small number of possible values. Let's see, my transaction ID is 10000031 so lets spoof a transaction with 10000032 and see what happens. . .
And even if transaction IDs aren't exposed now, you can almost guarantee that at some point some moron will accidentally, or purposefully expost them in the future.
Effective random number generation is CS101. Why leave such things up to the DB and auto-incrementers?
Admin
Perl originally only had global variables. Then, someone pointed out to Larry, "WTF?" Larry didn't want to break existing code (well, too much, anyway), so he added the "local" keyword. It, of course, indicated that the variable so declared was dynamically scoped. So, someone else asked of Larry, "WTF?" Larry replied by adding the "my" keyword, and lexically scoped variables ("Yay!"). Then, he added "our", leaving us to wonder about the second and third person. WTF?
Admin
my mouth hangs open - I don't know what to say to this.
Admin
Except that leaves open the possibility of two simultaneous calls getting the same max value and the first insert to be submitted to the DB will work, while the other will fail.
Admin
While we're at it can we ban the phrase 'the real wtf is...'.
Admin
Looks like someone found a bug....
Admin
Looks like someone found a bug.... (found floating just under No one likes the second page. on the second page)
what only 1............ as pointed out many times here this forum software is the biggist cluster of bugs most of us have ever seen!!!!!
Admin
If nothing else, at least keep track of ids used in a separate control table, then your new id will be max(id) + 1 each time. Make sure you implement locking on the control table while calculating your next id.
Admin
There are a number of ways to pull this off, depending on the front-end language you're using. This is a very common problem and .NET has implemented a (kinda) solution behind the scenes for you, which basically goes:
* Put everything into a multi-data-table dataset
Admin
Funny you mention FoxPro. My last job included a very large GUI app written in Visual Foxpro, where the lead designer thought it would be brillant to make all our primary keys *decimal values*. Specifically, they were of the form <unique integer value>.<branch number>, like 1.10001, 2.10001, 3.10001, etc. So we had the joy of figuring out how to get FoxPro to automatically call a VFP function that called a stored procedure that lookup up a table that contained the next value for each branch, build the decimal using some goofy math, and send it back up the chain.
I'm fairly sure the phrase "WTF" was spoken loudly and often during the early phases of this project.