- 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
Oh my god... This is probably the worst code I've seen posted on this site...
Admin
first post!
Admin
My guess is that it was some kind of crappy manual identity field management. Kinda like handling an identity by using MAX(IdField) + 1.
Admin
I wonder what would happen if they removed a user?
Admin
For me, project horror stories are pretty interesting, although, I agree that it probably isn't appropriate for this particular site. It can be a real drama to see how things wound up after such optimistic starts, what happened, what happened to the people on the project and the efforts to save it. Sort of "The Dark Night of the Soul of a New Machine".
Admin
Punch line..
This is a punch in the face of any professional developer.
Admin
Its obvious that the 'designer' wanted to save the unnecessary space that would be used up by a UserId column in the table. You can always determine the UserId with the code below.
Admin
Oh... that's not good.
Admin
I'm assuming that userid needs to be unique, and that this code is somewhere in the useradd portion... And for each user that is added, there is a row added to some table, before userid gets set for that user.... really don't do alot of db programming tho... But, to me, that's the ONLY logical explanation for this garbage... Well, at least that I can think of right now...
Admin
I'm sure they found out the hard way, several times.
Admin
I would guess the code looks like:
<FONT face="Courier New" size=2>set @strName = 'Wiley Hacker'
insert into users (name, ...) values (@strName, ...)
select * from users where 1 = 1
set @intUserId = @@rowcount
update users set UserId = @intUserId where name = @strName</FONT>
Sure he could use identity fields, if he knew they existed. But this adds the excitement of concurrency issues and the performance hit of retrieving the whole table to get the row count.
Admin
And since that onerous line of code is still in there, I suspect they worked their way out of the ensuing mess by manually whacking at records until everything was back as it should be, and then patting themselves on the back for another job well done.
I've seen it happen. More than once.
Admin
That was my guess too. I must confess that I've done crap like max(id)+1 or select count(*)+1 from blah when needing to do ad hoc inserts to a table that has a primary key field int rather than identity. It doesn't seem too bad because it was done on the dev database where concurrency is not expected and when we migrate to production, we shut down the applications first the the DBA runs the diff script generated by Adept SQL.
I'm guessing the context is some dinky admin application to allow a single admin to insert a new user and assign an id. This doesnt sound like it is necessarily that bad. What do others think, is it really that bad?
yes, no, unknown, not specified, null, filenotfound, aSuffusionOfYellow
Admin
The line of code is quite telling. My compliments go to Mr. Harris on his obviously excellent summarising skill.
Sincerely,
Gene Wirchenko
Admin
Everytime I read through these things, I just feel DIRTY! I have to go and answer a few questions at Experts Exchange to make me feel better.
Admin
OMG... i hope this is a small, rarely changed table. (No, I won't try to explain or even excuse that. I've already had my dose of trolling today ;-)
Admin
This is really not enough to work with.
At best it could be a silly but relatively harmless line of code. On the other hand, it could cause some major problems. I'm guessing major problems since Rick Harris took the time to submit this.
In the future, please provide us with a little bit more detail.
Or at least post a follow up after a few hours with more information.
This is one of the highlights of my day and I want to be able to grasp just how the much the code sucks.
Thanks.
Admin
Maybe he was trying to attach child records (like user preferences) to the user record and was having trouble figuring out how to reliably determine what the autonum generated for the inserted record. I seem to recall it wasn't always obvious how to get that number (in fact, I do not know how to do it and would appreciate very much if someone could enlighten me). On my current project we've been using Guids and so it can be generated before the insert and passed to the insert parent statement and the insert children statements and I do not need to worry about the "what is the id of the row I inserted?" problem. Last project was Oracle and it used sequences and I was also sheltered from the problem. On my first project I knew the more senior developers were struggling with it and I do not know what they did about the problem.
Admin
If you're inserting a user into a database, it is always always always better to let the database figure out what number to use, through whatever auto-incrementing key you've setup to take care of the user table. That is the only way you can make sure it's unique.
I had a situation once, where I'd run a bunch of bug testing and added a bunch of users, then removed them, so the counter was set at a higher number than the last existing row. So some bright boy comes along and decides that he's too smart to add stuff to the table using the methods that were set up for that...he's going to write a query to dump all his new users directly into the tables. So he figures out the last entry, and inserts all his users, setting their FOREIGN KEYS in related tables to what he imagines their ID numbers will be. Then, when regular users get added, we get duplicate foreign keys. Total snafu.
Stuff like that is very easy to do with a database. Use built-in methods wherever possible.
Admin
It is a user stack, not a user table. This code is safe as long as you remove users only at the end of the table. If you want to remove a user that isn't the last user on the table, you just remove the
users after him in the reverse order, remove the user in question, and re-add all other users to the table. You may do this easily using a recursive function. :D
Admin
Hi OneFactor,
Since an autonumber (or IDENTITY, as SQL Server calls it) should never be the only key for a table, the logical way is to read back the record using the business key to get the generated identity value.
Alternatives are SCOPE_IDENTITY(), @@IDENTITY, and IDENT_CURRENT('table_name'). They are described in Books Online.
Best, Hugo -trying hard to forget today's WTF code...
Admin
Depends on the database, but in SQLServer for example, you can use "SELECT @@IDENTITY" to get the last autogenerated key.
Admin
Maybe this was a contract for some touchy-feely government agency. They didn't want any users to feel alienated by having a different
ID than the other users. Did Rick Harris even look at the project requirements before submitting this code???
Admin
That's not scope sensitive, SCOPE_IDENTITY is
Admin
maybe for security reasons they can NEVER delete rows from the employee table.
maybe I'm going to rub some salt in my eyes and try to forget I ever saw this.
Admin
Yeah, I looked at that after making my post. It wasn't clear to me that any of the three options was robust. For Scope_Identity() it had a warning about triggers in the insert which would mean that it would bust if someone started created insert triggers. @@Identity and ident_current('table_name) seemed like it was rather "global" and vulnerable to concurrency issues if another transaction did an insert during the "lag time" between the original insert and the call to the identify variable or function. Perhaps I read the documentation wrong but my fears were not soothed by what I read. Finding three options each with differing warning labels made me nervous about the possibility of inadvertent error. I feel a bit like Brave Sir Robin running from the three-headed giant.
I noticed a lot of suggestions to generate the Guids before doing the insert (which is what we currently do on this project) and that has worked out very well for us. Asking for a Business Key feels like wishing Santa and his Reindeer would give me a lift to work every day so I can avoid traffic.
Guid, Guids, Guids, Guids, lovely Guids, wonderful Guids....
Ah, I know I should do development in Python instead ...
Admin
Didn't WoTC ban Lich because of stuff like this?
Admin
You are correct about @@identity and ident_current(), but not about scope_identity:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/tsqlref/ts_sa-ses_6n8p.asp
Scope_Identity() should always be used since it will alwasy return the identity from the correct scope even if triggers occur, but it wasn't available in SQL versions < 2000.
Admin
Some of the posts here mention doing the old "Count()+1" or "Max(ID)+1" techniques to get a new ID, but this @@ROWCOUNT method is even more ingenius because not only is it often wrong like the others, it is also exponentially less efficient!
Admin
Maybe they just wanted each user to feel like he was #1
Admin
The code that surround it was from my last project .. oh wait that was written in oracle, Hated it!!! I am glad I left for greener pastures!!!!
Run Run Run!!!
Admin
I dunno, without looking at surrounding code, that could just be a typo (@@ROWCOUNT instead of @@IDENTITY). Which is bad, but if it's unintentional, I don't think it qualifies.
Unlike this little morsel of evil I inherited, which obviously took some thought and a certain degree of skill:
SELECT ...
FROM ITINERARYITEMS AS I
JOIN HOTELS AS H
ON CONVERT (
INT
, SUBSTRING(
CONVERT (VARCHAR, I.Data)
, PATINDEX('%<var name=''id''><string>%', I.Data) + 23
, PATINDEX(
'%</string>%'
, SUBSTRING(
CONVERT (VARCHAR, I.Data)
, PATINDEX('%<var name=''id''><string>%', I.Data) + 23
, 20
)
) - 1
)
) = H.ID
...
WHERE BookingType = 'hotel'
AND I.STATE=1
AND PATINDEX('%<var name=''id''><string>%', Data) > 0
AND TIME BETWEEN @StartDate AND @EndDate
(names anonymized a bit and formatted so you can see what it's doing. The original was all on one line, naturally)
Yes, it's joining between an int column in one table and a number inside some XML in a text column in another! (and that number is stored in a <string> tag!?)
Admin
In my case, the parent table did not have an autonum or a built-in method. The table was expected "never to change". I may have even committed the crime of trying to derive what the foreign keys on child tables should be. Guids made all the problems go away.
Admin
YEAH! I'm FIRST!
I'm not, am I? Ahh well, I came pretty close. Why do some morons keep posting that? A lot of them aren't even first because people actually beat them to it.
And what's so great about being first, anyway? Do you really think you get any honour of being The First of one of the many posts on this site? What is it?
All I can say is it seems most people find it quite irritating. And if you wan't honour, then posting "FIRST" is probably the thing not to do.
Sorry, I, for one, am pretty irritated by all those "FIRST" posts (which are usually second posts).
Admin
But... presumably this would always return 1... assuming there was only one "JOE BLOW" in the database... The only place this makes sense is if you are adding a new user to the end of the table and.... no wait. IT NEVER MAKES SENSE![H]
Admin
Let's try that again...
And that last comment was supposed to be "and the number is in a <string> tag!?"
Admin
OK, I give up.
Admin
Why on earth would you submit that an IDENTITY should never be the only key for a table? That is (IMHO) assinine!
Admin
I thought it was Word of Command. I think the term was "mystifier" rather than "spoiler" which never got banned. I'm still sore over losing a two out of three match to a channel-lotus-mountain-fireball first round combo twice in a row. In hindsight I should have separated the Channeling from the Lotus when I shuffled the deck.
Though I suppose he lost more than I did as someone (not me) stole the Lotus when he wasn't looking.
Admin
At least you were the first one to complain. You win the kewpie doll. If there were more prizes available, perhaps the First Post winner could win something.
Admin
Why is it that nobody ever thought of having the generated ID returned as a parameter of the execute statement that executed the sql statement?
That is exactly because of this problem that we settled down for a table with an ID count. We set a lock on the table before picking up the number and release the hold once updated with the new value to prevent concurrency. The GetNewID procedure will retry to get a number for a certain time before giving up if the record is locked by another process.
Admin
Let me see if I got this straight: @@identity runs into trigger trouble, ident_current runs into concurrency issues, scope_identity does not work before sql server 2000. In that case, this @@rowcount garbage may be a result of a poor sap being stuck on an older Sql Server and not having a good solution available.
I guess I have another good interview question for supposed SQL experts now.
Admin
Doesn't SQL server have sequences?
SELECT curval('my_id_sequence) as newid;
Now the value of newid is mine, all mine. Nobody else will ever get it! (Until the sequences wraps around, anyway.)
Admin
Admin
SELECT curval('my_id_sequence) as newid;
Wouldn't that be "nextval"?
Admin
Why? Isn't it basically the same like count()? I aggree that max(ID) is much faster if ID is indexed, a reasonable though unsafe (considering the circumstances) assumption.
Admin
Which do you think is a more efficient way to return the # of rows in a table to the client?
Option 1: SELECT COUNT(*) FROM Table
Option 2: SELECT * FROM Table; SELECT @@ROWCOUNT
Admin
Take out the ELSE and I like it :)
Admin
Why? I guess I am a newb, but I have done this in situations where there were no other obvious columns to use as a primary key, and I have never run into any problems.
Admin
yeah, I agree, data integrity is assinine !