SQL Random number not working - sql

declare #fieldForceCounter as int
declare #SaleDate as dateTime
declare #RandomNoSeed as decimal
set #fieldForceCounter = 1
set #SaleDate = '1 Jan 2009'
set #RandomNoSeed = 0.0
WHILE #fieldForceCounter <= 3
BEGIN
while #SaleDate <= '1 Dec 2009'
begin
INSERT INTO MonthlySales(FFCode, SaleDate, SaleValue) VALUES(#fieldForceCounter, #SaleDate, RAND(#RandomNoSeed))
set #saleDate = #saleDate + 1
set #RandomNoSeed = Rand(#RandomNoSeed) + 1
end
set #SaleDate = '1 Jan 2009'
set #fieldForceCounter = #fieldForceCounter + 1
END
GO
This T-SQL command was supposed to insert random values in the 'SaleValue'-column in the 'MonthlySales'-table.
But it is inserting '1' every time .
What can be the problem?

Two problems:
Firstly, the rand() function returns a number between 0 and 1.
Secondly, when rand() is called multiple times in the same query (e.g. for multiple rows in an update statement), it usually returns the same number (which I suspect your algorithm above is trying to solve, by splitting it into multiple calls)
My favourite way around the second problem is to use a function that's guaranteed to return a unique value each time, like newid(), convert it to varbinary, and use it as the seed :)
Edit: after some testing, it seems you'll need to try using a different datatype for #RandomNoSeed; float behaves somewhat different to decimal, but still approaches a fixed value, so I'd recommend avoiding the use of #RandomNoSeed altogether, and simply use:
INSERT INTO MonthlySales(FFCode, SaleDate, SaleValue)
VALUES(#fieldForceCounter, #SaleDate, RAND(convert(varbinary,newid())))

You have major issues here...
Decimal issues
The default precision/scale for decimal is 38,0. So you aren't storing any decimal part.
So you are only using RAND(0) for 1st iteration and RAND(1) for all subsequent iterations, which is 0.943597390424144 and 0.713591993212924
I can't recall how rounding/truncation applies, and I don't know what datatype SalesValue is, but rounding would give "1" every time.
Now, if you fix this and declare decimal correctly...
Seeding issues
RAND takes an integer seed. Seeding with 1.0001 or 1.3 or 1.999 gives the same value (0.713591993212924).
So, "Rand(1.713591993212924) + 1" = "RAND(1) + 1" = "1.713591993212924" for every subsequent iteration.
Back to square one...
To fix
Get rid of #RandomNoSeed
Either: Generate a random integer value using CHECKSUM(NEWID())
Or: generate a random float value using RAND() * CHECKSUM(NEWID()) (Don't care about seed now)

Just a guess, but often rand functions generate a number from 0-1. Try multiplying your random number by 10.

Related

update multiple rows with random 9 digit number using rand() function

I am trying to update multiple rows with random 9 digit number using the following code.
UPDATE SGT_EMPLOYER
SET SSN = (CONVERT(NUMERIC(10,0),RAND() * 899999999) + 100000000)
WHERE EMPLOYER_ACCOUNT_ID = 123456789;
Expected result: the query should update 300 rows with 300 random 9 digit numbers.
Actual: query is updating 300 rows with same number as the ran() function is executing only once.
Please help. Thank You.
As you already figured out yourself, RAND is a run-time constant function in SQL Server. It means that it is called once per statement and the generated value is used for each affected row.
There are other functions that are called for each row. Often people use NEWID usually together with CHECKSUM as a substitute for a random number, but I would not recommend it because the distribution of such random numbers is likely to be poor.
There is a good function specifically designed to generate random numbers: CRYPT_GEN_RANDOM. It is available since at least SQL Server 2008.
It generates a given number of random bytes.
In your case it would be convenient to have a random number as a float value in the range of [0;1], same as the value returned by RAND.
So, CRYPT_GEN_RANDOM(4) generates 4 random bytes as varbinary.
Convert them to int, divide by the maximum value of 32-bit integer (4294967295) and add 0.5 to shift the range from [-0.5;+0.5] to [0;1]:
(CAST(CRYPT_GEN_RANDOM(4) as int) / 4294967295.0 + 0.5)
Your query becomes:
UPDATE SGT_EMPLOYER
SET SSN =
CONVERT(NUMERIC(10,0),
(CAST(CRYPT_GEN_RANDOM(4) as int) / 4294967295.0 + 0.5) * 899999999.0 + 100000000.0)
WHERE EMPLOYER_ACCOUNT_ID = 123456789;
Yes, the rand() line will only be executed once, before the rows are being updated, not every time a row is updated.
You can use a Stored Procedure to update every row with (CONVERT(NUMERIC(10,0),RAND() * 899999999) + 100000000).
Sean Lange is 100% correct. However, if you want to quickly mask your SSN, perhaps the following using HashBytes() may help.
Example
Declare #Table table (SSN varchar(25))
Insert into #Table values
('070-99-12345'),
('123-45-67890')
Select SSN
,AsInt = abs(cast(HashBytes('MD5', SSN) as int))
From #Table
Returns
SSN AsInt
070-99-12345 508860145
123-45-67890 843256257

Divide By Zero - Can't see why this would fail

I have written a function just to work out the Pro-Rata Clawback on any amount me typing it out constantly.
However I get a Divide By Zero Error everytime it runs, I broke the function down very simply.
DECLARE #ProRataAmount DECIMAL(8,2), #DaysRemaining INT,#NetValue DECIMAL(8,2), #TimeOnRisk INTEGER
SET #NetValue = 29.00
SET #TimeOnRisk = 8
SET #DaysRemaining = (365 - #TimeOnRisk)
PRINT #DaysRemaining
PRINT #NetValue
SELECT #NetValue / (#DaysRemaining/365)
There is NO value there that is dividing by zero, so why is it returning said error?
Cheers.
An INT/INT will return an INT.
Try the following (notice the 365.0):
SELECT #NetValue / (#DaysRemaining/365.0)
Now, just in case #DaysRemaining is ZERO, you can still generate a Divide By Zero unless you trap it like so:
SELECT #NetValue / (NullIf(#DaysRemaining,0)/365.0)
Dividing an integer with an integer will cause the problem here:
SELECT #NetValue / (#DaysRemaining/365)
Here you will always divide by zero because #DaysRemaining is less than the divisor. You can change the calculation around to get past this easily while keeping the value as integer:
SELECT #NetValue * 365 / #DaysRemaining

SQL Server - Enforcing the number of digits on an int

I have a unique ID that I am generating program-side in the format CCYYMMDDxxxx, where xxxx is a 4 digit string that will auto increment, starting from 0001.
To calculate the next element, I have wrote part of a query which gets those 4 digits from the string using substring.
DECLARE #number int, #nextstring varchar(4)
SET #number = (SELECT CONVERT(int, SUBSTRING(Payment_ID, 9, 4), 103) FROM Orders)
I need to be able to increment it by 1, but keep it in 4 digit format. I came across the 'right' keyword, but I don't know how many 0's ill need to put in front of it.
Is there a nice way to do this without a bunch of IF's? Of course, I could calculate the length and put the respective number of 0's at the start, but that doesn't account for 9, 99, and 999.
I really think that an identity column is the best way to handle this . . . then assign the sequential number afterwards.
But, if you want to do this, you need to left pad the number. Here is a method to get the next id based on values in the table:
SELECT (LEFT(MAX(payment_id), 8) +
RIGHT('0000' + CAST(CAST(RIGHT(MAX(payment_id), 4) as int) + 1 as VARCHAR(255))
)
FROM Orders;
This does not verify that the id is long enough. Let me repeat: I think it is much better to use an identity column as the id and then construct whatever attributes you want (such as the number within a day) when you need that information.

execution modulo on a large number in t-sql

I need to calculate the remainder of one number divided by another. for instance with these numbers:
271011240311350356232122 % 97
When I just want to do that in a sql statement, it works like a charm:
select 271011240311350356232122 % 97;
But when I have that large number in a varchar variable, I can't seem to get the job done.
I can't convert it into a int or even bigint, because it's too large. I can't convert it into a real because you can't use the modulo operator on a real number.
Any ideas...?
If it is too big for bigint you can use NUMERIC(38,0)
DECLARE #Num VARCHAR(38) = '271011240311350356232122'
SELECT CAST(#Num AS NUMERIC(38,0)) % 97

How to reduce the float length

Using SQL Server 2000
I want to reduce the decimal length
Query
Select 23/12 as total
Output is showing as 1.99999999999
I don't want to round the value, I want to diplay like this 1.99
Tried Query
Select LEFT(23/12, LEN(23/12) - 3) as total
The above query is working only if there is decimal value like 12.444444, but if the total is single digit means like 12 or 4 or 11...., i am getting error at run time.
How to do this.
Need Query Help
There is a very simple solution. You can find it in BOL. Round takes an optional 3rd argument, which is round type. The values are round or truncate.
ROUND numeric_expression , length [ ,function ] )
...
function Is the type of operation to perform. function must be
tinyint, smallint, or int. When function is omitted or has a value of
0 (default), numeric_expression is rounded. When a value other than 0
is specified, numeric_expression is truncated.
So just do
Select ROUND(cast(23 as float)/12, 2, 1) as total
That gives 1.91. Note, if you were really seeing 1.999 - something is really wrong with your computer. 23/12 = 1.916666666(ad infinitum). You need to cast one of the numbers as float since sql is assuming they're integers and doing integer division otherwise. You can of course cast them both as float, but as long as one is float the other will be converted too.
Not terribly elegant, but works for all cases:
Select CONVERT(float,LEFT(CONVERT(nvarchar, 23.0/12.0),CHARINDEX('.',CONVERT(nvarchar, 23.0/12.0)) + 2)) as total
Scalar Function
-- Description: Truncate instead of rounding a float
-- SELECT dbo.TruncateNumber(23.0/12.0,2)
-- =============================================
CREATE FUNCTION TruncateNumber
(
-- Add the parameters for the function here
#inFloat float,
#numDecimals smallint
)
RETURNS float
AS
BEGIN
IF (#numDecimals < 0)
BEGIN
SET #numDecimals = 0
END
-- Declare the return variable here
RETURN CONVERT(float,LEFT(CONVERT(nvarchar, #inFloat),CHARINDEX('.',CONVERT(nvarchar, #inFloat)) + #numDecimals))
END
GO