Archive for the 'SQL Server' Category

How to return an empty set with named columns

I was writing a Sql Server stored procedure and wanted it to return an empty record set if an error occurred and I wanted the column headings to be the same as for the result set when there is no error.  For example, when there is no error the stored procedure would output:

UserName

UserID

UserSex

Joe

1234

Male

Jane

5678

Female

 

When an error occurred, the stored procedure would output a table with no records:

UserName

UserID

UserSex

 

So, what is an easy way to get this empty set?  The answer is a simple Where clause that will never be true (click here to see a case when 1=2 http://www.techiebreak.com/uncategorized/algebraic-proof-that-12/) :

 

 

SELECT NULL AS UserName, NULL AS UserID, NULL AS UserSex WHERE 1=2

 

And that’s it!  Note that the fact that I used “NULL” for a value is not important because the value will never acutally show up.

Easy Random Text Generator Using Selective Characters

I needed to create a user-defined function (UDF) in SQL Server that returned a random password.  My initial idea was to generate a random integer between some ASCII value ‘x’ and ASCII value ‘y,’ then concatenate several of these together and voila.  Upon looking at the ASCII chart, I realized that there was no acceptable “block” of values that I wanted to use.  I did not want to use punctuation marks or certain characters that are easily confused, such as: 0,o,O,l,1,i,I, etc.   Additionally, to prevent an obscene word from being randomly created, I wanted to eliminate all vowels.  So, my next thought was to create a look-up, cross reference table where a random ID value would correspond to a character in the table; however, this seemed like quite a bit of overhead for a simple user-defined function.  Finally, I decided to use a single text variable to hold all of the desired characters.  Then, I pulled random characters out of the variable base on the characters’ relative position inside the string of text. 

The T-SQL is as follows:

Note that the following UDF (fn_GetRandomPassword) uses the function MyRand() previously discussed here:

http://www.dbtechie.com/sql-server/user-defined-functions-udf/random-thoughts-on-sql-server/

 

– =============================================

– Description:      Create a random password of PasswordLength

    comprised of lowercase letters, not including [a,e,i,j,l,o,u] and

    including the numbers 2 through 8.  Maximum length = 100 chars.

– =============================================

CREATE FUNCTION [dbo].[fn_GetRandomPassword]

(

       @PasswordLength INT

)

RETURNS varchar(100)

AS

BEGIN

       DECLARE @retVal VARCHAR(100)

       DECLARE @charList VARCHAR(26)

       DECLARE @rnd FLOAT

       DECLARE @loopCounter INT

       DECLARE @randID INT

       DECLARE @oneChar CHAR(1)

 

 

       – build the charList with chars a-z

       SET @charList =

       SET @loopCounter=97

       WHILE @loopCounter<123

       BEGIN – while

              SET @charList = @charList + CHAR(@loopCounter)

              SET @loopCounter = @loopCounter + 1

       END –while

 

 

       –replace unwanted characters with numbers 2-8

 

       –replace a

       SET @charList = REPLACE(@charList,‘a’,‘2′)

 

       –replace e

       SET @charList = REPLACE(@charList,‘e’,‘3′)

 

       –replace i

       SET @charList = REPLACE(@charList,‘i’,‘4′)

 

       –replace j

       SET @charList = REPLACE(@charList,‘j’,‘5′)

 

       –replace l

       SET @charList = REPLACE(@charList,‘l’,‘6′)

 

       –replace o

       SET @charList = REPLACE(@charList,‘o’,‘7′)

 

       –replace u

       SET @charList = REPLACE(@charList,‘u’,‘8′)

      

       – force passwordLength to be a legitimate value

       IF @PasswordLength >100

              SET @PasswordLength=100

 

       IF @PasswordLength <1

              SET @PasswordLength =1

      

 

       – build random password

       SET @loopCounter=0

       SET @retVal =

 

       WHILE @loopCounter < @PasswordLength

       BEGIN – while

 

              SET @rnd = (Select dbo.MyRand())  –Get random float from 0-1

 

              SET @randID = ROUND(25 * @rnd,0) + 1   –Get random int from 1 to 26

              SET @oneChar = SUBSTRING(@charList,@randID,1)

              SET @retVal = @retVal + @oneChar

              SET @loopCounter = @loopCounter + 1

       END – while

 

       RETURN @retVal

 

END

How to Reference Multiple Databases on the Same Server

Creating a SELECT statement that references multiple databases on the same server in SQL Server is simple, but the syntax is not obvious and not well documented.  Without the proper syntax, you will get the SQL message:  “Invalid object name.”  The correct syntax is as follows:

USE Database1

GO

 

SELECT     *

FROM       Database2.dbo.TableName

The only real trick is remembering to include the “dbo” in the correct spot.  You will, of course, have to have permission to both databases.

Random Thoughts on SQL Server

Recently, I created a user-defined SQL Server function to return random characters to be used as an initial password when signing up for an account.   Without knowing a few things about the RAND() function in advance, this fifteen-minute project could take you several hours.   The actual code for creating the random password I will discuss in a future article (http://www.dbtechie.com/sql-server/user-defined-functions-udf/easy-random-text-generator-using-selective-characters/)    In this article, I will discuss a few of the pitfalls of the RAND() function.

                                        

Why can’t I use RAND() in a user-defined function and how do I get around it?

The first thing to know is that the RAND() function (which returns a float between 0 and 1) is one of the nondeterministic built-in functions that cannot be called from a user-defined function (UDF).  Per msdn (http://msdn.microsoft.com/en-us/library/ms178091(SQL.90).aspx), “Deterministic functions always return the same result any time they are called with a specific set of input values and given the same state of the database. Nondeterministic functions may return different results each time they are called with a specific set of input values even if the database state that they access remains the same.”  For a list of nondeterministic functions that can and cannot be used in UDF, see this msdn link:  http://msdn.microsoft.com/en-us/library/ms191320(SQL.90).aspx.

If you tried using RAND() in your user-defined function, you probably saw the message, “Invalid use of side-effecting or time-dependent operator in ‘rand’ within a function.”   In other words, RAND() is one of the nondeterministic functions that SQL won’t let you use in a UDF.  The solution is simple.  Put the RAND() function in a view and refer to the view in the UDF.

CREATE VIEW [dbo].[vw_RandomFloat]

AS

SELECT     RAND() AS RandValue

—–

CREATE FUNCTION MyRand()

RETURNS FLOAT

AS

BEGIN

      RETURN (select RandValue from dbo.vw_RandomFloat)

END

—–

SELECT dbo.MyRand()

 

Why aren’t my values random?

Using RAND() without parameters  works just fine if you only plan on getting one random value per transaction.  RAND() is considered a pseudo-random function, because it depends on a seed value to determine where to start returning a predetermined sequence of values.   The same seed value will always return the same sequence of values.  RAND() is “seeded” with an integer to randomize the values returned.  The classic way to randomize is by seeding using a function from the internal clock of the computer, which is how the RAND() function will seed if you do not specify a seed value.   The problem is that if you include the RAND() function in a SELECT statement that returns multiple rows, the same seed value is used for every row in the result set of the SELECT statement.

We now find ourselves with the paradoxical problem of needing to find a way to create a random integer to seed the random function.  At first, you may think to use an identity column as a seed, but you will quickly be discouraged with this method because seed values that are close in value also return values that are close in value.  For example, RAND(1) returns 0.713591993212924 and RAND(2) returns 0.713610626184182.  These values are close enough that, for all intents and purposes , they might as well be the same number.

The best solution I have seen is to seed using the NEWID() function.  NEWID() returns a uniqueidentifier, so there is still a tiny bit of work left to convert it to an integer to seed the RAND() function.   The easiest is by using the CHECKSUM() function: Checksum(NEWID()).  You could also use the CAST() function: CAST(CAST(NEWID() AS VARBINARY) AS INT).

So, the new and improved random view would be:

CREATE VIEW [dbo].[vw_RandomFloat]

AS

SELECT     RAND(Checksum(NEWID())) AS RandValue