Assign a unique number in a range randomly - sql

I need to assign a list of employees a unique number from 1 to 1000 (or however many employees there are). The numbers can't repeat and need to be in a random order against the employee list. The numbers also need to be regenerated each week so the employee is assigned a new number.
I have tried to use the following, however the script is slow and it returns the same number for all employees.
DECLARE #Random INT;
DECLARE #Upper INT;
DECLARE #Lower INT
SET #Lower = 1
SET #Upper = 1000
SELECT #Random = ROUND(((#Upper - #Lower -1) * RAND() + #Lower), 0)
select personnum, firstnm, lastnm, (SELECT #Random)
from person
Can anyone shed any light on how to do this?
Thanks

Works on mssql server 2005+
select personnum, firstnm, lastnm, row_number() over (order by newid()) randomnumber
from person

Create a function for it. Here is one we use:
ALTER FUNCTION [dbo].[fn_GenerateUniqueNumber]()
RETURNS char(10)
AS
BEGIN
--DECLARE VARIABLES
DECLARE #RandomNumber VARCHAR(10)
DECLARE #I SMALLINT
DECLARE #RandNumber FLOAT
DECLARE #Position TINYINT
DECLARE #ExtractedCharacter VARCHAR(1)
DECLARE #ValidCharacters VARCHAR(255)
DECLARE #VCLength INT
DECLARE #Length INT
--SET VARIABLES VALUE
SET #ValidCharacters = '0123456789'
SET #VCLength = LEN(#ValidCharacters)
SET #ExtractedCharacter = ''
SET #RandNumber = 0
SET #Position = 0
SET #RandomNumber = ''
SET #Length = 10
SET #I = 1
WHILE #I < ( #Length + 1 )
BEGIN
SET #RandNumber = (SELECT RandNumber FROM [RandNumberView])
SET #Position = CONVERT(TINYINT, ( ( #VCLength - 1 ) * #RandNumber + 1 ))
SELECT #ExtractedCharacter = SUBSTRING(#ValidCharacters, #Position, 1)
SET #I = #I + 1
SET #RandomNumber = #RandomNumber + #ExtractedCharacter
END
RETURN #RandomNumber
END
To use it do something like this:
SELECT personnum, firstnm, lastnm,dbo.fn_GenerateUniqueNumber()
FROM person
You can modify the parameters and allowed values to ensure it is the type of number you want.

Related

How to add extremely large numbers in SQL Server

I have numbers in SQL Server that stored in string format
These values are in 0 and 1s only
declare #a varchar(max) = '100101001010100111010001010101011101010110100001010010111001'
declare #b varchar(max) = '010101100101010010101101001010100010100011010000101000100010'
a and b length can reach 100,000 digits
I want to add these 2 variables as numeric
Something like
#a + #b
And the result should be
110202101111110121111102011111111111110121110001111010211011
You can see this is not a binary adding... there are 2s
How can I do this in SQL Server?
I tried this
declare #a varchar(max) = '100101001010100111010001010101011101010110100001010010111001'
declare #b varchar(max) = '010101100101010010101101001010100010100011010000101000100010'
declare #ai bigint = cast(#a as bigint)
declare #bi bigint = cast(#b as bigint)
SELECT #ai + #bi
but I got this error
Msg 8115, Level 16, State 2, Line 4
Arithmetic overflow error converting expression to data type bigint.
Msg 8115, Level 16, State 2, Line 5
Arithmetic overflow error converting expression to data type bigint.
How can I do that?
You'd have to create your own calculator.
Some complicating factors, that the code below takes into consideration:
losing leading zeroes when converting number to string
carry over most significant digit from one batch to the next in case of overflow
For example:
declare #a varchar(max) = '100101001010100111010001010101011101010110100001010010311001'
declare #b varchar(max) = '010101100101010010101101001010100010100011010000101000900010'
declare #e varchar(max) = '110202101111110121111102011111111111110121110001111011211011' -- expected
declare #r varchar(max) = '' -- result
declare #batch_size int -- amount of digits to process at once
set #batch_size=18
declare #sum varchar(19) -- must be bigger than #batch_size
declare #carry bigint
set #carry = 0
declare #length int
set #length = LEN(#a) -- assumes LEN(#a) = LEN(#b)
declare #i int
set #i = #length / #batch_size
if #length % #batch_size = 0
set #i = #i - 1
while #i >= 0 begin
if #i * #batch_size + #batch_size > #length begin
set #a = #a + REPLICATE('0', #batch_size - #length % #batch_size)
set #b = #b + REPLICATE('0', #batch_size - #length % #batch_size)
end
set #sum = CAST(SUBSTRING(#a, #i * #batch_size + 1, #batch_size) AS bigint)
+ CAST(SUBSTRING(#b, #i * #batch_size + 1, #batch_size) AS bigint)
+ #carry
set #carry = 0
if LEN(#sum) > #batch_size begin
set #carry = SUBSTRING(#sum, 1, 1)
set #sum = SUBSTRING(#sum, 2, #batch_size)
end
if LEN(#sum) < #batch_size
set #sum = REPLICATE('0', #batch_size - LEN(#sum)) + #sum
if #i * #batch_size + #batch_size > #length
set #sum = SUBSTRING(#sum, 1, #length - #i * #batch_size)
set #r = #sum + #r
set #i = #i - 1
end
if #carry > 0
print 'overflow error'
if #r <> #e
print 'not the correct result'
select substring(#r,1,#length) as sum_of_a_and_b
You can use FLOAT(53):
declare #a varchar(max) = '100101001010100111010001010101011101010110100001010010111001'
declare #b varchar(max) = '010101100101010010101101001010100010100011010000101000100010'
declare #ai FLOAT(53) = cast(#a as FLOAT(53))
declare #bi FLOAT(53) = cast(#b as FLOAT(53))
The result of SELECT #ai + #b will be 1.1020210111111E+59
This will work for your sample input. But 100.000 digits will be impossible as numeric data type.
db<>fiddle
As your input digits are restricted to 1 and 0 you can do the following.
First create a numbers table with at least as many rows in it as your longest string.
CREATE TABLE dbo.Numbers(Number INT PRIMARY KEY WITH (DATA_COMPRESSION = ROW));
INSERT dbo.Numbers
SELECT TOP 100000 ROW_NUMBER() OVER (ORDER BY ##SPID)
FROM sys.all_columns c1, sys.all_columns c2
And then you can do
declare #a varchar(max) = '100101001010100111010001010101011101010110100001010010111001'
declare #b varchar(max) = '10101100101010010101101001010100010100011010000101000100010'
declare #c varchar(max)
SELECT #c = STRING_AGG(0 + SUBSTRING(normalised.a, Number, 1) + SUBSTRING(normalised.B, Number, 1), '') WITHIN GROUP (ORDER BY Number)
FROM dbo.Numbers
CROSS APPLY(SELECT LEN(#a), LEN(#b)) lengths(len_a, len_b)
/*If #a and #b are not equal length add zeroes to left pad out the shorter one*/
CROSS APPLY (SELECT CONCAT(REPLICATE('0', len_b-len_a),#a), CONCAT(REPLICATE('0', len_a-len_b),#b)) normalised(a,b)
WHERE Number <= LEN(normalised.a)
PRINT #c

Extract largest number from a string in T-SQL

I am importing working with data imported from excel files. There is a column with a string that can contain multiple numbers. I am trying to extract the largest number in the string or a 0 if there is no string.
The strings are in formats similar to:
"100% post-consumer recycled paper, 50% post-consumer recycled cover, 90% post-consumer recycled wire."
"Paper contains 30% post-consumer content."
or sometimes a empty string or null.
Given the irregular formatting of the string I am having trouble and any help would be appreciated.
Here's a scalar function that will take a string as an input and return the largest whole number it finds (up to a maximum of 3 digits, but from your question I've assumed you're dealing with percentages. If you need more digits, repeat the IF statements ad infinitum).
Paste this into SSMS and run it to create the function. To call it, do something like:
SELECT dbo.GetLargestNumberFromString(MyStringField) as [Largest Number in String]
FROM MyMessedUpData
Function:
CREATE FUNCTION GetLargestNumberFromString
(
#s varchar(max)
)
RETURNS int
AS
BEGIN
DECLARE #LargestNumber int, #i int
SET #i = 1
SET #LargestNumber = 0
WHILE #i <= LEN(#s)
BEGIN
IF SUBSTRING(#s, #i, 3) like '[0-9][0-9][0-9]'
BEGIN
IF CAST(SUBSTRING(#s, #i,3) as int) > #LargestNumber OR #LargestNumber IS NULL
SET #LargestNumber = CAST(SUBSTRING(#s, #i,3) as int);
END
IF SUBSTRING(#s, #i, 2) like '[0-9][0-9]'
BEGIN
IF CAST(SUBSTRING(#s, #i,2) as int) > #LargestNumber OR #LargestNumber IS NULL
SET #LargestNumber = CAST(SUBSTRING(#s, #i,2) as int);
END
IF SUBSTRING(#s, #i, 1) like '[0-9]' OR #LargestNumber IS NULL
BEGIN
IF CAST(SUBSTRING(#s, #i,1) as int) > #LargestNumber
SET #LargestNumber = CAST(SUBSTRING(#s, #i,1) as int);
END
SET #i = #i + 1
CONTINUE
END
RETURN #LargestNumber
END
Pull the data into SQL as-is
Write a query to get a distinct list of options in that column
Add a new column to store the desired value
Write an update statement to populate the new column
As far as determining the largest size, I think you need to look at your data set first, but the update could be as simple as:
DECLARE #COUNTER INT=1000
While EXISTS (SELECT * FROM <Table> WHERE NewColumn is NULL) AND #COUNTER>=0
BEGIN
UPDATE <Table> SET NewColumn=#COUNTER WHERE <SearchColumn> LIKE '%' + CONVERT(VARCHAR,#COUNTER) + '%' AND NewColumn is NULL
SET #COUNTER=#COUNTER-1
END
SQL Fiddle Demo
Generate the LEN(txt) possible RIGHT() fragments of txt. Trim each fragment at the first non-digit character. Test if the remainder is an int. Return the MAX().
SELECT
txt
,MAX(TRY_CONVERT(int,LEFT(RIGHT(txt,i),PATINDEX('%[^0-9]%',RIGHT(txt,i)+' ')-1)))
FROM MyTable
CROSS APPLY (
SELECT TOP(LEN(txt)) ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) i FROM master.dbo.spt_values a, master.dbo.spt_values b
) x
GROUP BY txt
I ended up creating a function that handled it. Here is the code:
CREATE FUNCTION [dbo].[cal_GetMaxPercentFromString]
RETURNS float
AS
BEGIN
declare #Numbers Table(number float)
insert into #Numbers
Select 0
declare #temp as varchar(2000) = #string
declare #position int, #length int, #offset int
WHILE CHARINDEX('%', #temp) > 0
BEGIN
set #position = CHARINDEX('%', #temp)
set #offset = 1
set #length = -1
WHILE #position - #offset > 0 and #length < 0
BEGIN
if SUBSTRING(#temp, #position - #offset, 1) not LIKE '[0-9]'
set #length = #offset - 1
set #offset = #offset + 1
END
if #length > 0
BEGIN
insert into #Numbers
select CAST(SUBSTRING(#temp, #position - #length, #length) as float)
END
set #temp = SUBSTRING(#temp, 1, #position - 1) + SUBSTRING(#temp, #position + 1, LEN(#temp) - #position)
END
declare #return as float
select #return = MAX(number) from #Numbers
return #return
END

return separate character from a string

How to return all the characters from a string and count it in sql.
if the string is "how are you"
it should return
char count
2
h 1
o 2
w 1
a 1
r 1
e 1
y 1
u 1
You can use this script. It will give you exactly what you need.
This one counts just the letters in the string.
declare #c int
declare #ch varchar(10)
declare #str varchar(max)
set #str = 'how are you'
declare #letter int
declare #i int
set #i = 1
create table #tbl(ch varchar(10), cnt int)
while (#i <= len(#str))
begin
set #letter = 0
set #ch = substring(#str, #i, 1)
select #c = count(*) from #tbl
where ch = #ch
if ( (#ch >= 'a' and #ch <= 'z') or (#ch >= 'A' and #ch <= 'Z') )
begin
set #letter = 1
end
if (#c = 0)
begin
if (#letter = 1)
begin
insert into #tbl (ch, cnt) values (#ch, 1)
end
end
else
begin
update #tbl set cnt = cnt + 1 where ch = #ch
end
set #i = #i + 1
end
select * from #tbl
drop table #tbl
And if you want to count all chars (not just letters),
this makes it even easier. Use this script.
declare #c int
declare #ch varchar(10)
declare #str varchar(max)
set #str = 'how are you'
declare #i int
set #i = 1
create table #tbl(ch varchar(10), cnt int)
while (#i <= len(#str))
begin
set #ch = substring(#str, #i, 1)
select #c = count(*) from #tbl
where ch = #ch
if (#c = 0)
begin
insert into #tbl (ch, cnt) values (#ch, 1)
end
else
begin
update #tbl set cnt = cnt + 1 where ch = #ch
end
set #i = #i + 1
end
select * from #tbl
drop table #tbl
You can use a customer tsql function, see http://gallery.technet.microsoft.com/scriptcenter/T-SQL-Script-to-Split-a-308206f3.
And you can make a query solve your issue using group by and count statements ?
This will return the result set you have requested. It does this by taking each letter and adding it to a new row within a temporary table and then querying the results to return the counts for each occurrence of the character.
DECLARE #individual CHAR(1);
DECLARE #text NVARCHAR(200)
SET #text = 'how are you';
IF OBJECT_ID('tempdb..#tmpTable') IS NOT NULL
DROP TABLE #tmpTable
CREATE TABLE #tmpTable (letter char(1));
WHILE LEN(#text) > 0
BEGIN
SET #individual = SUBSTRING(#text, 1, 2)
INSERT INTO #tmpTable (letter) VALUES (#individual);
SET #text = SUBSTRING(#text, 2, LEN(#text))
END
SELECT letter, COUNT(*) AS [count]
FROM #tmpTable
GROUP BY letter;

T-SQL - compare strings char by char

I need to compare two strings character by character using T-SQL. Let's assume i have twor strings like these:
123456789
212456789
Every time the character DO NOT match, I would like to increase the variable #Diff +=1. In this case the first three characters differ. So the #Diff = 3 (0 would be default value).
Thank you for all suggestions.
for columns in table you don't want to use row by row approach, try this one:
with cte(n) as (
select 1
union all
select n + 1 from cte where n < 9
)
select
t.s1, t.s2,
sum(
case
when substring(t.s1, c.n, 1) <> substring(t.s2, c.n, 1) then 1
else 0
end
) as diff
from test as t
cross join cte as c
group by t.s1, t.s2
=>sql fiddle demo
This code should count the differences in input strings and save this number to counter variable and display the result:
declare #var1 nvarchar(MAX)
declare #var2 nvarchar(MAX)
declare #i int
declare #counter int
set #var1 = '123456789'
set #var2 = '212456789'
set #i = LEN(#var1)
set #counter = 0
while #i > 0
begin
if SUBSTRING(#var1, #i, 1) <> SUBSTRING(#var2, #i, 1)
begin
set #counter = #counter + 1
end
set #i = #i - 1
end
select #counter as Value
The below query compares, shows the different characters and bring you the count of differences
Declare #char1 nvarchar(1), #char2 nvarchar(1), #i int = 1, #max int
Declare #string1 nvarchar(max) = '123456789'
, #string2 nvarchar(max) = '212456789'
Declare #diff_table table (pos int , string1 nvarchar(50) , string2 nvarchar(50), Status nvarchar(50))
Set #max = (select case when len(#String1+'x')-1 > len(#string2+'x')-1 then len(#String1+'x')-1 else len(#string2+'x')-1 end)
while #i < #max +1
BEGIN
Select #char1 = SUBSTRING(#string1,#i,1), #char2 = SUBSTRING(#string2,#i,1)
INSERT INTO #diff_table values
(
#i,
case when UNICODE(#char1) is null then '' else concat(#char1,' - (',UNICODE(#char1),')') end,
case when UNICODE(#char2) is null then '' else concat(#char2,' - (',UNICODE(#char2),')') end,
case when ISNULL(UNICODE(#char1),0) <> isnull(UNICODE(#char2),0) then 'CHECK' else 'OK' END
)
set #i+=1
END
Select * from #diff_table
Declare #diff int = (Select count(*) from #diff_table where Status = 'Check')
Select #diff 'Difference'
The output will be like this:

How to check upper case existence length in a string - Sql Query

How to check upper case existence length in a string using Sql Query?
For Eg:
1.KKart - from this string the result should be 2, because it has 2 upper case letters.
2.WPOaaa - from this string the result should be 3, because it has 3 upper case letters.
Thanks in advance
There is no built-in T-SQL function for that.
You can use a user-defined function like this one:
CREATE FUNCTION CountUpperCase
(
#input nvarchar(50)
)
RETURNS int
AS
BEGIN
declare #len int
declare #i int
declare #count int
declare #ascii int
set #len = len(#input)
set #i = 1
set #count = 0
while #i <= #len
begin
set #ascii = ascii(substring(#input, #i, 1))
if #ascii >= 65 and #ascii <= 90
begin
set #count = #count +1
end
set #i = #i + 1
end
return #count
END
Usage (with the examples from your question):
select dbo.CountUpperCase('KKart') returns 2.
select dbo.CountUpperCase('WPOaaa') returns 3.
How about something like this :
SELECT len(replace(my_string_field,'abcdefghijklmnopqrstuvwxyz','')) as 'UpperLen'
FROM my_table
The principle is simply to replace all lower case char by nothing and counts the remaining.