So, I’ve been answering questions on StackOverflow for two days now, and I still like the experience. However, I have noticed a disturbing trend in several of the questions being asked that involve SQL Server. Here’s an example of one; I’ll give you the question, and my answer, and then tell you what’s bugging me.
TSQL Generate 5 character length string, all digits [0-9] that doesn’t already exist in database
What’s the best way to do this?
I need to generate a 5 digit length string where all the characters are numeric. However, I need to be able to do this ‘x’ amount of times (user variable) and store this random strings in a database. Furthermore, I can’t generate the same string twice. Old strings will be removed after 6 months.
Pseudo-code
DECLARE @intIterator INT,
@intMax
SET @intIterator = 1
SET @intMax = 5 (number of strings to generate)
WHILE @intIterator <= @intMax
BEGIN
-- GENERATE RANDOM STRING OF 5 NUMERIC DIGITS
???
-- INSERT INTO DB IF DOESN'T ALREADY EXIST
INSERT INTO TSTRINGS
SELECT @RANDOMSTRING
IF @@ERROR = 0
SET @intIterator = @intIterator + 1
ENDI know this probably isn’t the best way to do it, so advice is appreciated. But really looking for ideas on how to generate the numeric 5 length strings.
I think this is a great question; the guy’s obviously looking for a list of randomly generated five-character strings comprised entirely of digits; this means there are only 100,000 possibilities. This question is tagged as SQL and TSQL, which means that he’s looking for a database-driven solution (I’m aware that there are several ways to skin this cat; I’m just showing you that he’s specifically looking for a SQL solution). Note, however, that he’s using a loop to create, validate, and insert 1 row at a time.
Here’s where a lot of reading lately on numbers tables comes into play; I think about this a bit, and post the following solution:
Here’s a set-based approach, using SQL 2005 syntax (would be a little easier with SQL 2008, but you didn’t specify). Also, if you had a numbers table, a large chunk of it can be cut out.
No looping, no duplicates, and should be nearly instantaneous (assuming the target column is indexed).
DECLARE @intMax INTEGER
SET @intMax = 5
INSERT INTO TSTRINGS
SELECT q.nString
FROM ( SELECT ROW_NUMBER() OVER ( ORDER BY ( NEWID() ) ) AS N2 ,
RIGHT(REPLICATE(‘0’, 5) + CONVERT(VARCHAR(5), N), 5) AS nString
FROM –the subquery below could be replaced by a numbers table
( SELECT TOP 100000
ROW_NUMBER() OVER ( ORDER BY ( ac1.Object_ID ) ) – 1 AS N
FROM Master.sys.columns ac1
CROSS JOIN Master.sys.columns ac2
CROSS JOIN Master.sys.columns ac3
) numbers
WHERE RIGHT(REPLICATE(‘0’, 5) + CONVERT(VARCHAR(5), N), 5) NOT IN ( SELECT nString
FROM TSTRINGS ) –check to see if reused
) q
WHERE q.N2 <= @intMax
A nice, tidy, set-based solution that I’ll probably keep in mind for generating passwords in the future (with a little work). So, what’s bugging me? Is it the fact that I have 0 votes for this solution? No, that’s not really it, because votes are kind of whimsical; some times you get them; sometimes you don’t.
No, what bugs me, is that out of the 6 answers posted, 3 of them involve CURSORS. For a SQL question. I know. Shock and horror.
Seriously, though; why do people still use cursors? I know that there are times when cursors are an appropriate tool, but you shouldn’t use them everyday. It’s kind of like buying those specialized drill bits for removing a stripped screw; for that specific application, they work great. If you leave them in your drill, and you try to use them in an everyday situation; guess what? You’ve stripped out a screw.
Let me be as clear as I can:
CURSORS SUCK, PEOPLE. HEAR ME? THEY SUCK.
There you go; I feel better now.
It’s simple: they haven’t made the paradigm shift.
Boy, you were quick! I was still cleaning up the code after it posted funny, and you’ve already replied 🙂
It just amazes me the number of posts that I’ve seen on there that the author thinks they need to do a cursor, and even more amazing is that people offer them cursor-based solutions. I understand when dealing with legacy code that you have to get dirty sometimes, but this is a prime question looking for a set-based solution.
Sheesh.
Tell us how you REALLY feel. 😉