How to update values using case statement - sql

I have created update statement like below
UPDATE dbo.S_Item
SET SalePrice3 = CASE WHEN Price <0 THEN '-1'
when Price=1 then 11
when Price=2 then 22
when Price=3 then 33
when Price=4 then 44
when Price=5 then 55
when Price=6 then 66
when Price=7 then 77
when Price=8 then 88
when Price=9 then 99
when Price=0 then 00
end
but i want update more values using above statement for example if want update price=123 it has to update 112233,if price=456 it has to update 445566,if price=725 it has to update 772255 how can achieve this help me

Create Function ReplicateDigits (
#Number Int)
Returns BigInt
Begin
Declare #Step SmallInt = 1,
#Result nVaRchar(100) = N''
While (#Step <= Len(#Number))
Begin
Select #Result = #Result + Replicate(SubString(Cast(#Number As Varchar), #Step, 1), 2)
Select #Step = #Step + 1
End
Return Cast(#Result As BigInt)
End
Go
Then:
UPDATE dbo.S_Item
SET SalePrice3 = CASE
WHEN Price <0 THEN '-1'
Else dbo.ReplicateDigits(Price)
End
Let me know if it was useful.

If the point is just in duplication of every digit, here's another implementation of the duplication method:
CREATE FUNCTION dbo.DuplicateDigits(#Input int)
RETURNS varchar(20)
AS
BEGIN
DECLARE #Result varchar(20) = CAST(#Input AS varchar(20));
DECLARE #Pos int = LEN(#Result);
WHILE #Pos > 0
BEGIN
SET #Result = STUFF(#Result, #Pos, 0, SUBSTRING(#Result, #Pos, 1));
SET #Pos -= 1;
END;
RETURN #Result;
END;
The method consists in iterating through the digits backwards, extracting each using SUBSTRING and duplicating it using STUFF.
And you would be using this function same as in Meysam Tolouee's answer:
UPDATE dbo.S_Item
SET SalePrice3 = CASE
WHEN Price < 0 THEN '-1'
ELSE dbo.DuplicateDigits(SalePrice3)
END;
To explain a little why the function's returned type is varchar, it is because that guarantees that the function returns the result no matter what the input's [reasonable] length is. The maximum length of 20 has been chosen merely because the input is [assumed to be] int and positive int values consist of up to 10 digits.
However, whether varchar(20) converts to the type of SalePrice3 is another matter, which should be considered separately.

Youy Must Create a Procedure for Achiving the Desired Result Rather Than to Use a Single Query.

Related

in SQL, how can I find duplicate string values within the same record?

Sample table
Record Number | Filter | Filters_Applied
----------------------------------------------
1 | yes | red, blue
2 | yes | green
3 | no |
4 | yes | red, red, blue
Is it possible to query all records where there are duplicate string values? For example, how could I query to pull record 4 where the string "red" appeared twice? Except in the table that I am dealing with, there are far more string values that can populate in the "filters_applied" column.
CLARIFICATION I am working out of Periscope and pulling data using SQL.
I assume that you have to check that in the logical page.
You can query the table with like '%red%'.
select Filters_Applied from table where Filters_Applied like '%red%';
You will get the data which has red at least one. Then, doing some string analysis in logic page.
In php, You can use the substr_count function to determine the number of occurrences of the string.
//the loop to load db query
while(){
$number= substr_count("Filters_Applied",red);
if($number>1){
echo "this".$Filters_Applied.">1"
}
}
for SQL-SERVER or other versions which can run these functions
Apply this logic
declare #val varchar(100) = 'yellow,blue,white,green'
DECLARE #find varchar(100) = 'green'
select #val = replace(#val,' ','') -- remove spaces
select #val;
select (len(#val)-len(replace(#val,#find,'')))/len(#find) [recurrence]
Create this Function which will parse string into rows and write query as given below. This will works for SQL Server.
CREATE FUNCTION [dbo].[StrParse]
(#delimiter CHAR(1),
#csv NTEXT)
RETURNS #tbl TABLE(Keys NVARCHAR(255))
AS
BEGIN
DECLARE #len INT
SET #len = Datalength(#csv)
IF NOT #len > 0
RETURN
DECLARE #l INT
DECLARE #m INT
SET #l = 0
SET #m = 0
DECLARE #s VARCHAR(255)
DECLARE #slen INT
WHILE #l <= #len
BEGIN
SET #l = #m + 1--current position
SET #m = Charindex(#delimiter,Substring(#csv,#l + 1,255))--next delimiter or 0
IF #m <> 0
SET #m = #m + #l
--insert #tbl(keys) values(#m)
SELECT #slen = CASE
WHEN #m = 0 THEN 255 --returns the remainder of the string
ELSE #m - #l
END --returns number of characters up to next delimiter
IF #slen > 0
BEGIN
SET #s = Substring(#csv,#l,#slen)
INSERT INTO #tbl
(Keys)
SELECT #s
END
SELECT #l = CASE
WHEN #m = 0 THEN #len + 1 --breaks the loop
ELSE #m + 1
END --sets current position to 1 after next delimiter
END
RETURN
END
GO
CREATE TABLE Table1# (RecordNumber int, [Filter] varchar(5), Filters_Applied varchar(100))
GO
INSERT INTO Table1# VALUES
(1,'yes','red, blue')
,(2,'yes','green')
,(3,'no ','')
,(4,'yes','red, red, blue')
GO
--This query will return what you are expecting
SELECT t.RecordNumber,[Filter],Filters_Applied,ltrim(rtrim(keys)), count(*)NumberOfRows
FROM Table1# t
CROSS APPLY dbo.StrParse (',', t.Filters_Applied)
GROUP BY t.RecordNumber,[Filter],Filters_Applied,ltrim(rtrim(keys)) HAVING count(*) >1
You didn't state your DBMS, but in Postgres this isn't that complicated:
select st.*
from sample_table st
join lateral (
select count(*) <> count(distinct trim(item)) as has_duplicates
from unnest(string_to_array(filters_applied,',')) as t(item)
) x on true
where x.has_duplicates;
Online example: http://rextester.com/TJUGJ44586
With the exception of string_to_array() the above is actually standard SQL

I have a column with datatype nvarchar and I want to sort it in ascending order. How do I achieve it in SSRS?

This is what I'm getting
abc 1
abc 12
abc 15
abc 2
abc 3
And this is how I want
abc 1
abc 2
abc 3
abc 12
abc 15
Query that I use:
select *
from view_abc
order by col1
Use a function to strip out the non numeric characters and leave just the value. Use another function to strip out all the numeric data. You can then sort on the two returned values.
It seems like a bit of work at first but once the functions are in you can re-use them in the future. Here's two functions I use regularly when we get data in from external sources and it's not very normalised.
They may not be the most efficient functions in the world but they work for my purposes
1st a function to just leave the numeric portion.
CREATE FUNCTION [fn].[StripToAlpha]
(
#inputString nvarchar(4000)
)
RETURNS varchar(4000)
AS
BEGIN
DECLARE #Counter as int
DECLARE #strReturnVal varchar(4000)
DECLARE #Len as int
DECLARE #ASCII as int
SET #Counter=0
SET #Len=LEN(#inputString)
SET #strReturnVal = ''
WHILE #Counter<=#Len
BEGIN
SET #Counter = #Counter +1
SET #ascii= ASCII(SUBSTRING(#inputString,#counter,1))
IF(#ascii BETWEEN 65 AND 90) OR (#ascii BETWEEN 97 AND 122)
BEGIN
SET #strReturnVal = #strReturnVal + (SUBSTRING(#inputString,#counter,1))
END
END
RETURN #strReturnVal
END
2nd a function to extract the value from a text field, this also handle percentages (e.g. abc 23% comes out as 0.23) but this is not required in your case.
You'll need to CREATE an 'fn' schema of change the schema name first...
CREATE FUNCTION [fn].[ConvertToValue]
(
#inputString nvarchar(4000)
)
RETURNS Float
AS
BEGIN
DECLARE #Counter as int
DECLARE #strReturnVal varchar(4000)
DECLARE #ReturnVal Float
DECLARE #Len as int
DECLARE #ASCII as int
SET #Counter=0
SET #Len=LEN(#inputString)
SET #strReturnVal = ''
IF #inputString IS NULL
BEGIN
Return NULL
END
IF #Len = 0 OR LEN(LTRIM(RTRIM(#inputString))) = 0
BEGIN
SET #ReturnVal=0
END
ELSE
BEGIN
WHILE #Counter<=#Len
BEGIN
SET #Counter = #Counter +1
SET #ascii= ASCII(SUBSTRING(#inputString,#counter,1))
IF(#ascii BETWEEN 48 AND 57) OR (#ascii IN (46,37))
BEGIN
SET #strReturnVal = #strReturnVal + (SUBSTRING(#inputString,#counter,1))
END
END
if RIGHT(#strReturnVal,1)='%'
BEGIN
SET #strReturnVal = LEFT(#strReturnVal,len(#strReturnVal)-1)
SET #strReturnVal = CAST((CAST(#strReturnVal AS FLOAT)/100) AS nvarchar(4000))
END
SET #ReturnVal = ISNULL(#strReturnVal,0)
END
RETURN #ReturnVal
END
Now we have the two functions created you can simply do
SELECT *
FROM view_abc
ORDER BY fn.StripToAlpha(Col1), fn.ConvertToValue(Col1)
Try this
Edited :
SELECT CAST(SUBSTRING(ColumnNameToOrder, CHARINDEX(' ', ColumnNameToOrder, 0), LEN (ColumnNameToOrder)) AS INT) AS IntColumn, SUBSTRING(ColumnNameToOrder,0, CHARINDEX(' ', ColumnNameToOrder, 0)) AS CharColumn, * FROM view_abc ORDER BY Charcolumn, Intcolumn
Instead of ColumnNameToOrder, you can put your column name which contains the data like 'abc 123'...
Tell me if it works please.
This is what I have come up with. Maybe it can help you, or at least point you in the right direction.
I tested the values. When the values have a zero, the order is like you would like the order to be. Like this:
abc 01
abc 02
abc 03
abc 12
abc 15
So you can run this query to update the existing values, to add the zero.
UPDATE abc
SET col1 = 'abc 0' + SUBSTRING(col1, 5, 1)
WHERE LEN(col1) = 5
Or you can do the above query like this if the first three characters can vary:
UPDATE abc
SET col1 = (SUBSTRING(col1, 1, 3) + ' 0' + SUBSTRING(col1, 5, 1))
WHERE col1 LIKE 'abc__'
This will override the existing value in the col1 column, only when the length of the current String is of length 5.
Then you can run the following query to get the results:
SELECT col1
FROM abc
ORDER BY col1 ASC
=cint(right(Fields!Col1.Value, instrrev(Fields!Col1.Value, " ")-1))
This will work in SSRS and sort correctly, but will only work if Col1 always contains a space, and that the characters after the space can be converted to an integer.

Is it possible to compare rows for similar data in SQL server

Is it possible to compare rows for similar data in SQL Server? I have a company name column in a table where company names could be somewhat similar. Here is an example of the different 8 values that represent the same 4 companies:
ANDORRA WOODS
ANDORRA WOODS HEALTHCARE CENTER
ABC HEALTHCARE, JOB #31181
ABC HEALTHCARE, JOB #31251
ACTION SERVICE SALES, A SUBSIDIARY OF SINGER EQUIPMENT
ACTION SERVICE SALES, A SUBSIDIARY OF SINGER EQUIPMENT COMPANY
APEX SYSTEMS
APEX SYSTEMS, INC
The way I clean it right now is using Google refine where I can identify clusters of similar data values and make them all as one.
Using this example I only need 4 names not 8 so I need to replace similar ones with only one since I will be assigning indexes to those names later on. Any help is greatly appreciated.
I have a couple UDF's I converted from some VB code some time ago that takes in 2 varchar() and returns an int between 0 and 100 (0= not similar, 100= same) if your interested.
-- Description: Removes any special characters from a string
CREATE FUNCTION [dbo].[SimReplaceSpecial]
(
-- Add the parameters for the function here
#String varchar(max)
)
RETURNS varchar(max)
AS
BEGIN
-- Declare the return variable here
DECLARE #Result varchar(max) = ''
-- Add the T-SQL statements to compute the return value here
DECLARE #Pos int = 1
DECLARE #Asc int
DECLARE #WorkingString varchar(max)
SET #WorkingString = upper(#String)
WHILE #Pos <= LEN(#WorkingString)
BEGIN
SET #Asc = ascii(substring(#WorkingString,#Pos,1))
If (#Asc >= 48 And #Asc <= 57) Or (#Asc >= 65 And #Asc <= 90)
SET #Result = #Result + Char(#Asc)
SET #Pos = #Pos + 1
--IF #Pos + 1 > len(#String)
-- BREAK
--ELSE
-- CONTINUE
END
-- Return the result of the function
RETURN #Result
END
-- Description: DO NOT CALL DIRECTLY - Used by the Similar function
-- Finds longest common substring (other than single
-- characters) in String1 and String2, then recursively
-- finds longest common substring in left-hand
-- portion and right-hand portion. Updates the
-- cumulative score.
CREATE FUNCTION [dbo].[SimFindCommon]
(
-- Add the parameters for the function here
#String1 varchar(max),
#String2 varchar(max),
#Score int
)
RETURNS int
AS
BEGIN
-- Declare the return variable here
--DECLARE #Result int
DECLARE #Longest Int = 0
DECLARE #StartPos1 Int = 0
DECLARE #StartPos2 Int = 0
DECLARE #J Int = 0
DECLARE #HoldStr varchar(max)
DECLARE #TestStr varchar(max)
DECLARE #LeftStr1 varchar(max) = ''
DECLARE #LeftStr2 varchar(max) = ''
DECLARE #RightStr1 varchar(max) = ''
DECLARE #RightStr2 varchar(max) = ''
-- Add the T-SQL statements to compute the return value here
SET #HoldStr = #String2
WHILE LEN(#HoldStr) > #Longest
BEGIN
SET #TestStr = #HoldStr
WHILE LEN(#TestStr) > 1
BEGIN
SET #J = CHARINDEX(#TestStr,#String1)
If #J > 0
BEGIN
--Test string is sub-set of the other string
If Len(#TestStr) > #Longest
BEGIN
--Test string is longer than previous
--longest. Store its length and position.
SET #Longest = Len(#TestStr)
SET #StartPos1 = #J
SET #StartPos2 = CHARINDEX(#TestStr,#String2)
END
--No point in going further with this string
BREAK
END
ELSE
--Test string is not a sub-set of the other
--string. Discard final character of test
--string and try again.
SET #TestStr = Left(#TestStr, LEN(#TestStr) - 1)
END
--Now discard first char of test string and
--repeat the process.
SET #HoldStr = Right(#HoldStr, LEN(#HoldStr) - 1)
END
--Update the cumulative score with the length of
--the common sub-string.
SET #Score = #Score + #Longest
--We now have the longest common sub-string, so we
--can isolate the sub-strings to the left and right
--of it.
If #StartPos1 > 3 And #StartPos2 > 3
BEGIN
SET #LeftStr1 = Left(#String1, #StartPos1 - 1)
SET #LeftStr2 = Left(#String2, #StartPos2 - 1)
If RTRIM(LTRIM(#LeftStr1)) <> '' And RTRIM(LTRIM(#LeftStr2)) <> ''
BEGIN
--Get longest common substring from left strings
SET #Score = dbo.SimFindCommon(#LeftStr1, #LeftStr2,#Score)
END
END
ELSE
BEGIN
SET #LeftStr1 = ''
SET #LeftStr2 = ''
END
If #Longest > 0
BEGIN
SET #RightStr1 = substring(#String1, #StartPos1 + #Longest, LEN(#String1))
SET #RightStr2 = substring(#String2, #StartPos2 + #Longest, LEN(#String2))
If RTRIM(LTRIM(#RightStr1)) <> '' And RTRIM(LTRIM(#RightStr2)) <> ''
BEGIN
--Get longest common substring from right strings
SET #Score = dbo.SimFindCommon(#RightStr1, #RightStr2,#Score)
END
END
ELSE
BEGIN
SET #RightStr1 = ''
SET #RightStr2 = ''
END
-- Return the result of the function
RETURN #Score
END
-- Description: Compares two not-empty strings regardless of case.
-- Returns a numeric indication of their similarity
-- (0 = not at all similar, 100 = identical)
CREATE FUNCTION [dbo].[Similar]
(
-- Add the parameters for the function here
#String1 varchar(max),
#String2 varchar(max)
)
RETURNS int
AS
BEGIN
-- Declare the return variable here
DECLARE #Result int
DECLARE #WorkingString1 varchar(max)
DECLARE #WorkingString2 varchar(max)
-- Add the T-SQL statements to compute the return value here
if isnull(#String1,'') = '' or isnull(#String2,'') = ''
SET #Result = 0
ELSE
BEGIN
--Convert each string to simplest form (letters
--and digits only, all upper case)
SET #WorkingString1 = dbo.SimReplaceSpecial(#String1)
SET #WorkingString2 = dbo.SimReplaceSpecial(#String2)
If RTRIM(LTRIM(#WorkingString1)) = '' Or RTRIM(LTRIM(#WorkingString2)) = ''
BEGIN
--One or both of the strings is now empty
SET #Result = 0
END
ELSE
BEGIN
If #WorkingString1 = #WorkingString2
BEGIN
--Strings are identical
SET #Result = 100
END
ELSE
BEGIN
--Find all common sub-strings
SET #Result = dbo.SimFindCommon(#WorkingString1, #WorkingString2,0)
--We now have the cumulative score. Return this
--as a percent of the maximum score. The maximum
--score is the average length of the two strings.
SET #Result = #Result * 200 / (Len(#WorkingString1) + Len(#WorkingString2))
END
END
END
-- Return the result of the function
RETURN #Result
END
--Usage--------------------------------------------------------------------
--Call the "Similar" Function only
SELECT dbo.Similar('ANDORRA WOODS','ANDORRA WOODS HEALTHCARE CENTER')
--Result = 60
SELECT dbo.Similar('ABC HEALTHCARE, JOB #31181','ABC HEALTHCARE, JOB #31251')
--Result = 85
SELECT dbo.Similar('ACTION SERVICE SALES, A SUBSIDIARY OF SINGER EQUIPMENT','ACTION SERVICE SALES, A SUBSIDIARY OF SINGER EQUIPMENT COMPANY')
--Result = 92
SELECT dbo.Similar('APEX SYSTEMS','APEX SYSTEMS, INC')
--Result = 88
SSIS/Data Tools has a Fuzzy Grouping transformation that is very helpful in situations like this. It doesn't actually group your data, rather it gives you similarity scores that you can use to determine when items should be grouped together.
Plenty of tutorials out there, here's one: The Fuzzy Grouping Transformation

Generating random strings with T-SQL

If you wanted to generate a pseudorandom alphanumeric string using T-SQL, how would you do it? How would you exclude characters like dollar signs, dashes, and slashes from it?
Using a guid
SELECT #randomString = CONVERT(varchar(255), NEWID())
very short ...
Similar to the first example, but with more flexibility:
-- min_length = 8, max_length = 12
SET #Length = RAND() * 5 + 8
-- SET #Length = RAND() * (max_length - min_length + 1) + min_length
-- define allowable character explicitly - easy to read this way an easy to
-- omit easily confused chars like l (ell) and 1 (one) or 0 (zero) and O (oh)
SET #CharPool =
'abcdefghijkmnopqrstuvwxyzABCDEFGHIJKLMNPQRSTUVWXYZ23456789.,-_!$##%^&*'
SET #PoolLength = Len(#CharPool)
SET #LoopCount = 0
SET #RandomString = ''
WHILE (#LoopCount < #Length) BEGIN
SELECT #RandomString = #RandomString +
SUBSTRING(#Charpool, CONVERT(int, RAND() * #PoolLength) + 1, 1)
SELECT #LoopCount = #LoopCount + 1
END
I forgot to mention one of the other features that makes this more flexible. By repeating blocks of characters in #CharPool, you can increase the weighting on certain characters so that they are more likely to be chosen.
When generating random data, specially for test, it is very useful to make the data random, but reproducible. The secret is to use explicit seeds for the random function, so that when the test is run again with the same seed, it produces again exactly the same strings. Here is a simplified example of a function that generates object names in a reproducible manner:
alter procedure usp_generateIdentifier
#minLen int = 1
, #maxLen int = 256
, #seed int output
, #string varchar(8000) output
as
begin
set nocount on;
declare #length int;
declare #alpha varchar(8000)
, #digit varchar(8000)
, #specials varchar(8000)
, #first varchar(8000)
declare #step bigint = rand(#seed) * 2147483647;
select #alpha = 'qwertyuiopasdfghjklzxcvbnm'
, #digit = '1234567890'
, #specials = '_## '
select #first = #alpha + '_#';
set #seed = (rand((#seed+#step)%2147483647)*2147483647);
select #length = #minLen + rand(#seed) * (#maxLen-#minLen)
, #seed = (rand((#seed+#step)%2147483647)*2147483647);
declare #dice int;
select #dice = rand(#seed) * len(#first),
#seed = (rand((#seed+#step)%2147483647)*2147483647);
select #string = substring(#first, #dice, 1);
while 0 < #length
begin
select #dice = rand(#seed) * 100
, #seed = (rand((#seed+#step)%2147483647)*2147483647);
if (#dice < 10) -- 10% special chars
begin
select #dice = rand(#seed) * len(#specials)+1
, #seed = (rand((#seed+#step)%2147483647)*2147483647);
select #string = #string + substring(#specials, #dice, 1);
end
else if (#dice < 10+10) -- 10% digits
begin
select #dice = rand(#seed) * len(#digit)+1
, #seed = (rand((#seed+#step)%2147483647)*2147483647);
select #string = #string + substring(#digit, #dice, 1);
end
else -- rest 80% alpha
begin
declare #preseed int = #seed;
select #dice = rand(#seed) * len(#alpha)+1
, #seed = (rand((#seed+#step)%2147483647)*2147483647);
select #string = #string + substring(#alpha, #dice, 1);
end
select #length = #length - 1;
end
end
go
When running the tests the caller generates a random seed it associates with the test run (saves it in the results table), then passed along the seed, similar to this:
declare #seed int;
declare #string varchar(256);
select #seed = 1234; -- saved start seed
exec usp_generateIdentifier
#seed = #seed output
, #string = #string output;
print #string;
exec usp_generateIdentifier
#seed = #seed output
, #string = #string output;
print #string;
exec usp_generateIdentifier
#seed = #seed output
, #string = #string output;
print #string;
Update 2016-02-17: See the comments bellow, the original procedure had an issue in the way it advanced the random seed. I updated the code, and also fixed the mentioned off-by-one issue.
Use the following code to return a short string:
SELECT SUBSTRING(CONVERT(varchar(40), NEWID()),0,9)
If you are running SQL Server 2008 or greater, you could use the new cryptographic function crypt_gen_random() and then use base64 encoding to make it a string. This will work for up to 8000 characters.
declare #BinaryData varbinary(max)
, #CharacterData varchar(max)
, #Length int = 2048
set #BinaryData=crypt_gen_random (#Length)
set #CharacterData=cast('' as xml).value('xs:base64Binary(sql:variable("#BinaryData"))', 'varchar(max)')
print #CharacterData
I'm not expert in T-SQL, but the simpliest way I've already used it's like that:
select char((rand()*25 + 65))+char((rand()*25 + 65))
This generates two char (A-Z, in ascii 65-90).
select left(NEWID(),5)
This will return the 5 left most characters of the guid string
Example run
------------
11C89
9DB02
For one random letter, you can use:
select substring('ABCDEFGHIJKLMNOPQRSTUVWXYZ',
(abs(checksum(newid())) % 26)+1, 1)
An important difference between using newid() versus rand() is that if you return multiple rows, newid() is calculated separately for each row, while rand() is calculated once for the whole query.
Here is a random alpha numeric generator
print left(replace(newid(),'-',''),#length) //--#length is the length of random Num.
There are a lot of good answers but so far none of them allow a customizable character pool and work as a default value for a column. I wanted to be able to do something like this:
alter table MY_TABLE add MY_COLUMN char(20) not null
default dbo.GenerateToken(crypt_gen_random(20))
So I came up with this. Beware of the hard-coded number 32 if you modify it.
-- Converts a varbinary of length N into a varchar of length N.
-- Recommend passing in the result of CRYPT_GEN_RANDOM(N).
create function GenerateToken(#randomBytes varbinary(max))
returns varchar(max) as begin
-- Limit to 32 chars to get an even distribution (because 32 divides 256) with easy math.
declare #allowedChars char(32);
set #allowedChars = 'abcdefghijklmnopqrstuvwxyz012345';
declare #oneByte tinyint;
declare #oneChar char(1);
declare #index int;
declare #token varchar(max);
set #index = 0;
set #token = '';
while #index < datalength(#randomBytes)
begin
-- Get next byte, use it to index into #allowedChars, and append to #token.
-- Note: substring is 1-based.
set #index = #index + 1;
select #oneByte = convert(tinyint, substring(#randomBytes, #index, 1));
select #oneChar = substring(#allowedChars, 1 + (#oneByte % 32), 1); -- 32 is the number of #allowedChars
select #token = #token + #oneChar;
end
return #token;
end
This worked for me: I needed to generate just three random alphanumeric characters for an ID, but it could work for any length up to 15 or so.
declare #DesiredLength as int = 3;
select substring(replace(newID(),'-',''),cast(RAND()*(31-#DesiredLength) as int),#DesiredLength);
Another simple solution with the complete alphabet:
SELECT LEFT(REPLACE(REPLACE((SELECT CRYPT_GEN_RANDOM(16) FOR XML PATH(''), BINARY BASE64),'+',''),'/',''),16);
Replace the two 16's with the desired length.
Sample result:
pzyMATe3jJwN1XkB
I realize that this is an old question with many fine answers. However when I found this I also found a more recent article on TechNet by Saeid Hasani
T-SQL: How to Generate Random Passwords
While the solution focuses on passwords it applies to the general case. Saeid works through various considerations to arrive at a solution. It is very instructive.
A script containing all the code blocks form the article is separately available via the TechNet Gallery, but I would definitely start at the article.
For SQL Server 2016 and later, here is a really simple and relatively efficient expression to generate cryptographically random strings of a given byte length:
--Generates 36 bytes (48 characters) of base64 encoded random data
select r from OpenJson((select Crypt_Gen_Random(36) r for json path))
with (r varchar(max))
Note that the byte length is not the same as the encoded size; use the following from this article to convert:
Bytes = 3 * (LengthInCharacters / 4) - Padding
I came across this blog post first, then came up with the following stored procedure for this that I'm using on a current project (sorry for the weird formatting):
CREATE PROCEDURE [dbo].[SpGenerateRandomString]
#sLength tinyint = 10,
#randomString varchar(50) OUTPUT
AS
BEGIN
SET NOCOUNT ON
DECLARE #counter tinyint
DECLARE #nextChar char(1)
SET #counter = 1
SET #randomString = ”
WHILE #counter <= #sLength
BEGIN
SELECT #nextChar = CHAR(48 + CONVERT(INT, (122-48+1)*RAND()))
IF ASCII(#nextChar) not in (58,59,60,61,62,63,64,91,92,93,94,95,96)
BEGIN
SELECT #randomString = #randomString + #nextChar
SET #counter = #counter + 1
END
END
END
I did this in SQL 2000 by creating a table that had characters I wanted to use, creating a view that selects characters from that table ordering by newid(), and then selecting the top 1 character from that view.
CREATE VIEW dbo.vwCodeCharRandom
AS
SELECT TOP 100 PERCENT
CodeChar
FROM dbo.tblCharacter
ORDER BY
NEWID()
...
SELECT TOP 1 CodeChar FROM dbo.vwCodeCharRandom
Then you can simply pull characters from the view and concatenate them as needed.
EDIT: Inspired by Stephan's response...
select top 1 RandomChar from tblRandomCharacters order by newid()
No need for a view (in fact I'm not sure why I did that - the code's from several years back). You can still specify the characters you want to use in the table.
I use this procedure that I developed simply stipluate the charaters you want to be able to display in the input variables, you can define the length too.
Hope this formats well, I am new to stack overflow.
IF EXISTS (SELECT * FROM sys.objects WHERE type = 'P' AND object_id = OBJECT_ID(N'GenerateARandomString'))
DROP PROCEDURE GenerateARandomString
GO
CREATE PROCEDURE GenerateARandomString
(
#DESIREDLENGTH INTEGER = 100,
#NUMBERS VARCHAR(50)
= '0123456789',
#ALPHABET VARCHAR(100)
='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz',
#SPECIALS VARCHAR(50)
= '_=+-$£%^&*()"!#~#:',
#RANDOMSTRING VARCHAR(8000) OUT
)
AS
BEGIN
-- Author David Riley
-- Version 1.0
-- You could alter to one big string .e.e numebrs , alpha special etc
-- added for more felxibility in case I want to extend i.e put logic in for 3 numbers, 2 pecials 3 numbers etc
-- for now just randomly pick one of them
DECLARE #SWAP VARCHAR(8000); -- Will be used as a tempoary buffer
DECLARE #SELECTOR INTEGER = 0;
DECLARE #CURRENTLENGHT INTEGER = 0;
WHILE #CURRENTLENGHT < #DESIREDLENGTH
BEGIN
-- Do we want a number, special character or Alphabet Randonly decide?
SET #SELECTOR = CAST(ABS(CHECKSUM(NEWID())) % 3 AS INTEGER); -- Always three 1 number , 2 alphaBET , 3 special;
IF #SELECTOR = 0
BEGIN
SET #SELECTOR = 3
END;
-- SET SWAP VARIABLE AS DESIRED
SELECT #SWAP = CASE WHEN #SELECTOR = 1 THEN #NUMBERS WHEN #SELECTOR = 2 THEN #ALPHABET ELSE #SPECIALS END;
-- MAKE THE SELECTION
SET #SELECTOR = CAST(ABS(CHECKSUM(NEWID())) % LEN(#SWAP) AS INTEGER);
IF #SELECTOR = 0
BEGIN
SET #SELECTOR = LEN(#SWAP)
END;
SET #RANDOMSTRING = ISNULL(#RANDOMSTRING,'') + SUBSTRING(#SWAP,#SELECTOR,1);
SET #CURRENTLENGHT = LEN(#RANDOMSTRING);
END;
END;
GO
DECLARE #RANDOMSTRING VARCHAR(8000)
EXEC GenerateARandomString #RANDOMSTRING = #RANDOMSTRING OUT
SELECT #RANDOMSTRING
Sometimes we need a lot of random things: love, kindness, vacation, etc.
I have collected a few random generators over the years, and these are from Pinal Dave and a stackoverflow answer I found once. Refs below.
--Adapted from Pinal Dave; http://blog.sqlauthority.com/2007/04/29/sql-server-random-number-generator-script-sql-query/
SELECT
ABS( CAST( NEWID() AS BINARY( 6)) %1000) + 1 AS RandomInt
, CAST( (ABS( CAST( NEWID() AS BINARY( 6)) %1000) + 1)/7.0123 AS NUMERIC( 15,4)) AS RandomNumeric
, DATEADD( DAY, -1*(ABS( CAST( NEWID() AS BINARY( 6)) %1000) + 1), GETDATE()) AS RandomDate
--This line from http://stackoverflow.com/questions/15038311/sql-password-generator-8-characters-upper-and-lower-and-include-a-number
, CAST((ABS(CHECKSUM(NEWID()))%10) AS VARCHAR(1)) + CHAR(ASCII('a')+(ABS(CHECKSUM(NEWID()))%25)) + CHAR(ASCII('A')+(ABS(CHECKSUM(NEWID()))%25)) + LEFT(NEWID(),5) AS RandomChar
, ABS(CHECKSUM(NEWID()))%50000+1 AS RandomID
This will produce a string 96 characters in length, from the Base64 range (uppers, lowers, numbers, + and /). Adding 3 "NEWID()" will increase the length by 32, with no Base64 padding (=).
SELECT
CAST(
CONVERT(NVARCHAR(MAX),
CONVERT(VARBINARY(8), NEWID())
+CONVERT(VARBINARY(8), NEWID())
+CONVERT(VARBINARY(8), NEWID())
+CONVERT(VARBINARY(8), NEWID())
+CONVERT(VARBINARY(8), NEWID())
+CONVERT(VARBINARY(8), NEWID())
+CONVERT(VARBINARY(8), NEWID())
+CONVERT(VARBINARY(8), NEWID())
+CONVERT(VARBINARY(8), NEWID())
,2)
AS XML).value('xs:base64Binary(xs:hexBinary(.))', 'VARCHAR(MAX)') AS StringValue
If you are applying this to a set, make sure to introduce something from that set so that the NEWID() is recomputed, otherwise you'll get the same value each time:
SELECT
U.UserName
, LEFT(PseudoRandom.StringValue, LEN(U.Pwd)) AS FauxPwd
FROM Users U
CROSS APPLY (
SELECT
CAST(
CONVERT(NVARCHAR(MAX),
CONVERT(VARBINARY(8), NEWID())
+CONVERT(VARBINARY(8), NEWID())
+CONVERT(VARBINARY(8), NEWID())
+CONVERT(VARBINARY(8), NEWID())
+CONVERT(VARBINARY(8), NEWID())
+CONVERT(VARBINARY(8), NEWID())
+CONVERT(VARBINARY(8), NEWID())
+CONVERT(VARBINARY(8), NEWID())
+CONVERT(VARBINARY(8), NEWID())
+CONVERT(VARBINARY(8), NEWID())
+CONVERT(VARBINARY(8), U.UserID) -- Causes a recomute of all NEWID() calls
,2)
AS XML).value('xs:base64Binary(xs:hexBinary(.))', 'VARCHAR(MAX)') AS StringValue
) PseudoRandom
CREATE OR ALTER PROC USP_GENERATE_RANDOM_CHARACTER ( #NO_OF_CHARS INT, #RANDOM_CHAR VARCHAR(40) OUTPUT)
AS
BEGIN
SELECT #RANDOM_CHAR = SUBSTRING (REPLACE(CONVERT(VARCHAR(40), NEWID()), '-',''), 1, #NO_OF_CHARS)
END
/*
USAGE:
DECLARE #OUT VARCHAR(40)
EXEC USP_GENERATE_RANDOM_CHARACTER 13,#RANDOM_CHAR = #OUT OUTPUT
SELECT #OUT
*/
Small modification of Remus Rusanu code -thanks for sharing
This generate a random string and can be used without the seed value
declare #minLen int = 1, #maxLen int = 612, #string varchar(8000);
declare #length int;
declare #seed INT
declare #alpha varchar(8000)
, #digit varchar(8000)
, #specials varchar(8000)
, #first varchar(8000)
declare #step bigint = rand() * 2147483647;
select #alpha = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
, #digit = '1234567890'
, #specials = '_##-/\ '
select #first = #alpha + '_#';
set #seed = (rand(#step)*2147483647);
select #length = #minLen + rand(#seed) * (#maxLen-#minLen)
, #seed = (rand((#seed+#step)%2147483647)*2147483647);
declare #dice int;
select #dice = rand(#seed) * len(#first),
#seed = (rand((#seed+#step)%2147483647)*2147483647);
select #string = substring(#first, #dice, 1);
while 0 < #length
begin
select #dice = rand(#seed) * 100
, #seed = (rand((#seed+#step)%2147483647)*2147483647);
if (#dice < 10) -- 10% special chars
begin
select #dice = rand(#seed) * len(#specials)+1
, #seed = (rand((#seed+#step)%2147483647)*2147483647);
select #string = #string + substring(#specials, #dice, 1);
end
else if (#dice < 10+10) -- 10% digits
begin
select #dice = rand(#seed) * len(#digit)+1
, #seed = (rand((#seed+#step)%2147483647)*2147483647);
select #string = #string + substring(#digit, #dice, 1);
end
else -- rest 80% alpha
begin
declare #preseed int = #seed;
select #dice = rand(#seed) * len(#alpha)+1
, #seed = (rand((#seed+#step)%2147483647)*2147483647);
select #string = #string + substring(#alpha, #dice, 1);
end
select #length = #length - 1
end
SELECT #string
Heres something based on New Id.
with list as
(
select 1 as id,newid() as val
union all
select id + 1,NEWID()
from list
where id + 1 < 10
)
select ID,val from list
option (maxrecursion 0)
I thought I'd share, or give back to the community ...
It's ASCII based, and the solution is not perfect but it works quite well.
Enjoy,
Goran B.
/*
-- predictable masking of ascii chars within a given decimal range
-- purpose:
-- i needed an alternative to hashing alg. or uniqueidentifier functions
-- because i wanted to be able to revert to original char set if possible ("if", the operative word)
-- notes: wrap below in a scalar function if desired (i.e. recommended)
-- by goran biljetina (2014-02-25)
*/
declare
#length int
,#position int
,#maskedString varchar(500)
,#inpString varchar(500)
,#offsetAsciiUp1 smallint
,#offsetAsciiDown1 smallint
,#ipOffset smallint
,#asciiHiBound smallint
,#asciiLoBound smallint
set #ipOffset=null
set #offsetAsciiUp1=1
set #offsetAsciiDown1=-1
set #asciiHiBound=126 --> up to and NOT including
set #asciiLoBound=31 --> up from and NOT including
SET #inpString = '{"config":"some string value", "boolAttr": true}'
SET #length = LEN(#inpString)
SET #position = 1
SET #maskedString = ''
--> MASK:
---------
WHILE (#position < #length+1) BEGIN
SELECT #maskedString = #maskedString +
ISNULL(
CASE
WHEN ASCII(SUBSTRING(#inpString,#position,1))>#asciiLoBound AND ASCII(SUBSTRING(#inpString,#position,1))<#asciiHiBound
THEN
CHAR(ASCII(SUBSTRING(#inpString,#position,1))+
(case when #ipOffset is null then
case when ASCII(SUBSTRING(#inpString,#position,1))%2=0 then #offsetAsciiUp1 else #offsetAsciiDown1 end
else #ipOffset end))
WHEN ASCII(SUBSTRING(#inpString,#position,1))<=#asciiLoBound
THEN '('+CONVERT(varchar,ASCII(SUBSTRING(#Inpstring,#position,1))+1000)+')' --> wrap for decode
WHEN ASCII(SUBSTRING(#inpString,#position,1))>=#asciiHiBound
THEN '('+CONVERT(varchar,ASCII(SUBSTRING(#inpString,#position,1))+1000)+')' --> wrap for decode
END
,'')
SELECT #position = #position + 1
END
select #MaskedString
SET #inpString = #maskedString
SET #length = LEN(#inpString)
SET #position = 1
SET #maskedString = ''
--> UNMASK (Limited to within ascii lo-hi bound):
-------------------------------------------------
WHILE (#position < #length+1) BEGIN
SELECT #maskedString = #maskedString +
ISNULL(
CASE
WHEN ASCII(SUBSTRING(#inpString,#position,1))>#asciiLoBound AND ASCII(SUBSTRING(#inpString,#position,1))<#asciiHiBound
THEN
CHAR(ASCII(SUBSTRING(#inpString,#position,1))+
(case when #ipOffset is null then
case when ASCII(SUBSTRING(#inpString,#position,1))%2=1 then #offsetAsciiDown1 else #offsetAsciiUp1 end
else #ipOffset*(-1) end))
ELSE ''
END
,'')
SELECT #position = #position + 1
END
select #maskedString
This uses rand with a seed like one of the other answers, but it is not necessary to provide a seed on every call. Providing it on the first call is sufficient.
This is my modified code.
IF EXISTS (SELECT * FROM sys.objects WHERE type = 'P' AND object_id = OBJECT_ID(N'usp_generateIdentifier'))
DROP PROCEDURE usp_generateIdentifier
GO
create procedure usp_generateIdentifier
#minLen int = 1
, #maxLen int = 256
, #seed int output
, #string varchar(8000) output
as
begin
set nocount on;
declare #length int;
declare #alpha varchar(8000)
, #digit varchar(8000)
, #specials varchar(8000)
, #first varchar(8000)
select #alpha = 'qwertyuiopasdfghjklzxcvbnm'
, #digit = '1234567890'
, #specials = '_##$&'
select #first = #alpha + '_#';
-- Establish our rand seed and store a new seed for next time
set #seed = (rand(#seed)*2147483647);
select #length = #minLen + rand() * (#maxLen-#minLen);
--print #length
declare #dice int;
select #dice = rand() * len(#first);
select #string = substring(#first, #dice, 1);
while 0 < #length
begin
select #dice = rand() * 100;
if (#dice < 10) -- 10% special chars
begin
select #dice = rand() * len(#specials)+1;
select #string = #string + substring(#specials, #dice, 1);
end
else if (#dice < 10+10) -- 10% digits
begin
select #dice = rand() * len(#digit)+1;
select #string = #string + substring(#digit, #dice, 1);
end
else -- rest 80% alpha
begin
select #dice = rand() * len(#alpha)+1;
select #string = #string + substring(#alpha, #dice, 1);
end
select #length = #length - 1;
end
end
go
In SQL Server 2012+ we could concatenate the binaries of some (G)UIDs and then do a base64 conversion on the result.
SELECT
textLen.textLen
, left((
select CAST(newid() as varbinary(max)) + CAST(newid() as varbinary(max))
where textLen.textLen is not null /*force evaluation for each outer query row*/
FOR XML PATH(''), BINARY BASE64
),textLen.textLen) as randomText
FROM ( values (2),(4),(48) ) as textLen(textLen) --define lengths here
;
If you need longer strings (or you see = characters in the result) you need to add more + CAST(newid() as varbinary(max)) in the sub select.
Here's one I came up with today (because I didn't like any of the existing answers enough).
This one generates a temp table of random strings, is based off of newid(), but also supports a custom character set (so more than just 0-9 & A-F), custom length (up to 255, limit is hard-coded, but can be changed), and a custom number of random records.
Here's the source code (hopefully the comments help):
/**
* First, we're going to define the random parameters for this
* snippet. Changing these variables will alter the entire
* outcome of this script. Try not to break everything.
*
* #var {int} count The number of random values to generate.
* #var {int} length The length of each random value.
* #var {char(62)} charset The characters that may appear within a random value.
*/
-- Define the parameters
declare #count int = 10
declare #length int = 60
declare #charset char(62) = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
/**
* We're going to define our random table to be twice the maximum
* length (255 * 2 = 510). It's twice because we will be using
* the newid() method, which produces hex guids. More later.
*/
-- Create the random table
declare #random table (
value nvarchar(510)
)
/**
* We'll use two characters from newid() to make one character in
* the random value. Each newid() provides us 32 hex characters,
* so we'll have to make multiple calls depending on length.
*/
-- Determine how many "newid()" calls we'll need per random value
declare #iterations int = ceiling(#length * 2 / 32.0)
/**
* Before we start making multiple calls to "newid", we need to
* start with an initial value. Since we know that we need at
* least one call, we will go ahead and satisfy the count.
*/
-- Iterate up to the count
declare #i int = 0 while #i < #count begin set #i = #i + 1
-- Insert a new set of 32 hex characters for each record, limiting to #length * 2
insert into #random
select substring(replace(newid(), '-', ''), 1, #length * 2)
end
-- Now fill the remaining the remaining length using a series of update clauses
set #i = 0 while #i < #iterations begin set #i = #i + 1
-- Append to the original value, limit #length * 2
update #random
set value = substring(value + replace(newid(), '-', ''), 1, #length * 2)
end
/**
* Now that we have our base random values, we can convert them
* into the final random values. We'll do this by taking two
* hex characters, and mapping then to one charset value.
*/
-- Convert the base random values to charset random values
set #i = 0 while #i < #length begin set #i = #i + 1
/**
* Explaining what's actually going on here is a bit complex. I'll
* do my best to break it down step by step. Hopefully you'll be
* able to follow along. If not, then wise up and come back.
*/
-- Perform the update
update #random
set value =
/**
* Everything we're doing here is in a loop. The #i variable marks
* what character of the final result we're assigning. We will
* start off by taking everything we've already done first.
*/
-- Take the part of the string up to the current index
substring(value, 1, #i - 1) +
/**
* Now we're going to convert the two hex values after the index,
* and convert them to a single charset value. We can do this
* with a bit of math and conversions, so function away!
*/
-- Replace the current two hex values with one charset value
substring(#charset, convert(int, convert(varbinary(1), substring(value, #i, 2), 2)) * (len(#charset) - 1) / 255 + 1, 1) +
-- (1) -------------------------------------------------------^^^^^^^^^^^^^^^^^^^^^^^-----------------------------------------
-- (2) ---------------------------------^^^^^^^^^^^^^^^^^^^^^^11111111111111111111111^^^^-------------------------------------
-- (3) --------------------^^^^^^^^^^^^^2222222222222222222222222222222222222222222222222^------------------------------------
-- (4) --------------------333333333333333333333333333333333333333333333333333333333333333---^^^^^^^^^^^^^^^^^^^^^^^^^--------
-- (5) --------------------333333333333333333333333333333333333333333333333333333333333333^^^4444444444444444444444444--------
-- (6) --------------------5555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555^^^^----
-- (7) ^^^^^^^^^^^^^^^^^^^^66666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666^^^^
/**
* (1) - Determine the two hex characters that we'll be converting (ex: 0F, AB, 3C, etc.)
* (2) - Convert those two hex characters to a a proper hexadecimal (ex: 0x0F, 0xAB, 0x3C, etc.)
* (3) - Convert the hexadecimals to integers (ex: 15, 171, 60)
* (4) - Determine the conversion ratio between the length of #charset and the range of hexadecimals (255)
* (5) - Multiply the integer from (3) with the conversion ratio from (4) to get a value between 0 and (len(#charset) - 1)
* (6) - Add 1 to the offset from (5) to get a value between 1 and len(#charset), since strings start at 1 in SQL
* (7) - Use the offset from (6) and grab a single character from #subset
*/
/**
* All that is left is to add in everything we have left to do.
* We will eventually process the entire string, but we will
* take things one step at a time. Round and round we go!
*/
-- Append everything we have left to do
substring(value, 2 + #i, len(value))
end
-- Select the results
select value
from #random
It's not a stored procedure, but it wouldn't be that hard to turn it into one. It's also not horrendously slow (it took me ~0.3 seconds to generate 1,000 results of length 60, which is more than I'll ever personally need), which was one of my initial concerns from all of the string mutation I'm doing.
The main takeaway here is that I'm not trying to create my own random number generator, and my character set isn't limited. I'm simply using the random generator that SQL has (I know there's rand(), but that's not great for table results). Hopefully this approach marries the two kinds of answers here, from overly simple (i.e. just newid()) and overly complex (i.e. custom random number algorithm).
It's also short (minus the comments), and easy to understand (at least for me), which is always a plus in my book.
However, this method cannot be seeded, so it's going to be truly random each time, and you won't be able to replicate the same set of data with any means of reliability. The OP didn't list that as a requirement, but I know that some people look for that sort of thing.
I know I'm late to the party here, but hopefully someone will find this useful.
Based on various helpful responses in this article I landed with a combination of a couple options I liked.
DECLARE #UserId BIGINT = 12345 -- a uniqueId in my system
SELECT LOWER(REPLACE(NEWID(),'-','')) + CONVERT(VARCHAR, #UserId)
Below are the way to Generate 4 Or 8 Characters Long Random Alphanumeric String in SQL
select LEFT(CONVERT(VARCHAR(36),NEWID()),4)+RIGHT(CONVERT(VARCHAR(36),NEWID()),4)
SELECT RIGHT(REPLACE(CONVERT(VARCHAR(36),NEWID()),'-',''),8)
So I liked a lot of the answers above, but I was looking for something that was a little more random in nature. I also wanted a way to explicitly call out excluded characters. Below is my solution using a view that calls the CRYPT_GEN_RANDOM to get a cryptographic random number. In my example, I only chose a random number that was 8 bytes. Please note, you can increase this size and also utilize the seed parameter of the function if you want. Here is the link to the documentation: https://learn.microsoft.com/en-us/sql/t-sql/functions/crypt-gen-random-transact-sql
CREATE VIEW [dbo].[VW_CRYPT_GEN_RANDOM_8]
AS
SELECT CRYPT_GEN_RANDOM(8) as [value];
The reason for creating the view is because CRYPT_GEN_RANDOM cannot be called directly from a function.
From there, I created a scalar function that accepts a length and a string parameter that can contain a comma delimited string of excluded characters.
CREATE FUNCTION [dbo].[fn_GenerateRandomString]
(
#length INT,
#excludedCharacters VARCHAR(200) --Comma delimited string of excluded characters
)
RETURNS VARCHAR(Max)
BEGIN
DECLARE #returnValue VARCHAR(Max) = ''
, #asciiValue INT
, #currentCharacter CHAR;
--Optional concept, you can add default excluded characters
SET #excludedCharacters = CONCAT(#excludedCharacters,',^,*,(,),-,_,=,+,[,{,],},\,|,;,:,'',",<,.,>,/,`,~');
--Table of excluded characters
DECLARE #excludedCharactersTable table([asciiValue] INT);
--Insert comma
INSERT INTO #excludedCharactersTable SELECT 44;
--Stores the ascii value of the excluded characters in the table
INSERT INTO #excludedCharactersTable
SELECT ASCII(TRIM(value))
FROM STRING_SPLIT(#excludedCharacters, ',')
WHERE LEN(TRIM(value)) = 1;
--Keep looping until the return string is filled
WHILE(LEN(#returnValue) < #length)
BEGIN
--Get a truly random integer values from 33-126
SET #asciiValue = (SELECT TOP 1 (ABS(CONVERT(INT, [value])) % 94) + 33 FROM [dbo].[VW_CRYPT_GEN_RANDOM_8]);
--If the random integer value is not in the excluded characters table then append to the return string
IF(NOT EXISTS(SELECT *
FROM #excludedCharactersTable
WHERE [asciiValue] = #asciiValue))
BEGIN
SET #returnValue = #returnValue + CHAR(#asciiValue);
END
END
RETURN(#returnValue);
END
Below is an example of the how to call the function.
SELECT [dbo].[fn_GenerateRandomString](8,'!,#,#,$,%,&,?');
May this will be an answer to create random lower & upper characters. It's using a while loop which can be controlled to generate the string with a specific length.
--random string generator
declare #maxLength int = 5; --max length
declare #bigstr varchar(10) --uppercase character
declare #smallstr varchar(10) --lower character
declare #i int = 1;
While #i <= #maxLength
begin
set #bigstr = concat(#bigstr, char((rand()*26 + 65)));
set #smallstr = concat(#smallstr, char((rand()*26 + 96)));
set #i = len(#bigstr)
end
--select query
select #bigstr, #smallstr

T-SQL trim &nbsp (and other non-alphanumeric characters)

We have some input data that sometimes appears with &nbsp characters on the end.
The data comes in from the source system as varchar() and our attempts to cast as decimal fail b/c of these characters.
Ltrim and Rtrim don't remove the characters, so we're forced to do something like:
UPDATE myTable
SET myColumn = replace(myColumn,char(160),'')
WHERE charindex(char(160),myColumn) > 0
This works for the &nbsp, but is there a good way to do this for any non-alphanumeric (or in this case numeric) characters?
This will remove all non alphanumeric chracters
CREATE FUNCTION [dbo].[fnRemoveBadCharacter]
(
#BadString nvarchar(20)
)
RETURNS nvarchar(20)
AS
BEGIN
DECLARE #nPos INTEGER
SELECT #nPos = PATINDEX('%[^a-zA-Z0-9_]%', #BadString)
WHILE #nPos > 0
BEGIN
SELECT #BadString = STUFF(#BadString, #nPos, 1, '')
SELECT #nPos = PATINDEX('%[^a-zA-Z0-9_]%', #BadString)
END
RETURN #BadString
END
Use the function like:
UPDATE TableToUpdate
SET ColumnToUpdate = dbo.fnRemoveBadCharacter(ColumnToUpdate)
WHERE whatever
This page has a sample of how you can remove non-alphanumeric chars:
-- Put something like this into a user function:
DECLARE #cString VARCHAR(32)
DECLARE #nPos INTEGER
SELECT #cString = '90$%45623 *6%}~:#'
SELECT #nPos = PATINDEX('%[^0-9]%', #cString)
WHILE #nPos > 0
BEGIN
SELECT #cString = STUFF(#cString, #nPos, 1, '')
SELECT #nPos = PATINDEX('%[^0-9]%', #cString)
END
SELECT #cString
How is the table being populated? While it is possible to scrub this in sql a better approach would be to change the column type to int and scrub the data before it's loaded into the database (SSIS). Is this an option?
For large datasets I have had better luck with this function that checks the ASCII value. I have added options to keep only alpha, numeric or alphanumeric based on the parameters.
--CleanType 1 - Remove all non alpanumeric
-- 2 - Remove only alpha
-- 3 - Remove only numeric
CREATE FUNCTION [dbo].[fnCleanString] (
#InputString varchar(8000)
, #CleanType int
, #LeaveSpaces bit
) RETURNS varchar(8000)
AS
BEGIN
-- // Declare variables
-- ===========================================================
DECLARE #Length int
, #CurLength int = 1
, #ReturnString varchar(8000)=''
SELECT #Length = len(#InputString)
-- // Begin looping through each char checking ASCII value
-- ===========================================================
WHILE (#CurLength <= (#Length+1))
BEGIN
IF (ASCII(SUBSTRING(#InputString,#CurLength,1)) between 48 and 57 AND #CleanType in (1,3) )
or (ASCII(SUBSTRING(#InputString,#CurLength,1)) between 65 and 90 AND #CleanType in (1,2) )
or (ASCII(SUBSTRING(#InputString,#CurLength,1)) between 97 and 122 AND #CleanType in (1,2) )
or (ASCII(SUBSTRING(#InputString,#CurLength,1)) = 32 AND #LeaveSpaces = 1 )
BEGIN
SET #ReturnString = #ReturnString + SUBSTRING(#InputString,#CurLength,1)
END
SET #CurLength = #CurLength + 1
END
RETURN #ReturnString
END
If the mobile could start with a Plus(+) I will use the function like this
CREATE FUNCTION [dbo].[Mobile_NoAlpha](#Mobile VARCHAR(1000))
RETURNS VARCHAR(1000)
AS
BEGIN
DECLARE #StartsWithPlus BIT = 0
--check if the mobile starts with a plus(+)
IF LEFT(#Mobile, 1) = '+'
BEGIN
SET #StartsWithPlus = 1
--Take out the plus before using the regex to eliminate invalid characters
SET #Mobile = RIGHT(#Mobile, LEN(#Mobile)-1)
END
WHILE PatIndex('%[^0-9]%', #Mobile) > 0
SET #Mobile = Stuff(#Mobile, PatIndex('%[^0-9]%', #Mobile), 1, '')
IF #StartsWithPlus = 1
SET #Mobile = '+' + #Mobile
RETURN #Mobile
END