I am trying to write a function that will output some address information on a CIDR formatted IP (output underneath code):
create function dbo.ConvertIpToInt (#Ip as varchar(15))
returns bigint
as
begin
return (convert(bigint, parsename(#Ip, 1)) +
convert(bigint, parsename(#Ip, 2)) * 256 +
convert(bigint, parsename(#Ip, 3)) * 256 * 256 +
convert(bigint, parsename(#Ip, 4)) * 256 * 256 * 256)
end
go
create function dbo.ConvertIntToIp (#Int bigint)
returns varchar(15)
as
begin
declare
#IpHex varchar(8)
,#IpDotted varchar(15)
select
#IpHex = substring(convert(varchar(30), master.dbo.fn_varbintohexstr(#Int)), 11, 8)
select
#IpDotted = convert(varchar(3), convert(int, (convert(varbinary, substring(#IpHex, 1, 2), 2)))) + '.' +
convert(varchar(3), convert(int, (convert(varbinary, substring(#IpHex, 3, 2), 2)))) + '.' +
convert(varchar(3), convert(int, (convert(varbinary, substring(#IpHex, 5, 2), 2)))) + '.' +
convert(varchar(3), convert(int, (convert(varbinary, substring(#IpHex, 7, 2), 2))))
return #IpDotted
end
go
create function dbo.GetCidrIpRange (#CidrIp varchar(15))
returns #result table
(
CidrIp varchar(15) not null,
Mask int not null,
LowRange varchar(15) not null,
LowIp varchar(15) not null,
HighRange varchar(15) not null,
HighIp varchar(15) not null,
AddressQty bigint not null
)
as
begin
declare #Base bigint = cast(4294967295 as bigint)
declare #Mask int = cast(substring(#CidrIp, patindex('%/%' , #CidrIP) + 1, 2) as int)
declare #Power bigint = Power(2.0, 32.0 - #Mask) - 1
declare #LowRange bigint = dbo.ConvertIpToInt(left(#CidrIp, patindex('%/%' , #CidrIp) - 1)) & (#Base ^ #Power)
declare #HighRange bigint = #LowRange + #Power
insert #result
select
CidrIp = #CidrIp
, Mask = #Mask
, LowRange = #LowRange
, LowIp = dbo.ConvertIntToIp(#LowRange)
, HighRange = #HighRange
, HighIp = dbo.ConvertIntToIp(#HighRange)
, AddressQty = convert(bigint, power(2.0, (32.0 - #Mask)))
return
end
go
select * from dbo.GetCidrIpRange('195.65.254.11/2');
This outputs the following:
CidrIp Mask LowRange LowIp HighRange HighIp AddressQty
--------------------------------------------------------------------------------------
195.65.254.11/2 2 3221225472 192.0.0.0 4294967295 255.255.255.255 1073741824
I have been browsing SO and Google for some hours now, and I am quite convinced that ConvertIpToInt and ConvertIntToIp are correct.
However, I was expecting the following output:
CidrIp Mask LowRange LowIp HighRange HighIp AddressQty
--------------------------------------------------------------------------------------
195.65.254.11/2 2 3275881985 195.65.254.1 3275882238 195.65.254.254 254
Can someone please point me out where the mistake in my code is? I've been staring myself blind and I don't see it (or I am misunderstanding how to do this).
According to both http://www.ipaddressguide.com/cidr and http://jodies.de/ipcalc?host=195.65.254.11&mask1=2&mask2=, your calculations are correct. The only disagreement between those two sites is that the jodies.de/ipcalc page removes the lowest and highest (broadcast) IP addresses from the range.
I tested with both 195.65.254.11/2 and 195.65.254.11/24. In order to get your code working, I needed to change the input parameter specification on dbo.GetCidrIpRang to be VARCHAR(20) (as mentioned by #Damien_The_Unbeliever in a comment on the question).
Two notes regarding performance:
For the ConvertIpToInt and ConvertIntToIp Scalar UDFs you might be better off using the INET_AddressToNumber and INET_NumberToAddress functions, respectively, that are included in the Free version of the SQL# SQLCLR library (which I wrote, but hey, Free :). The reason for this recommendation is that unlike T-SQL UDFs, deterministic SQLCLR UDFs (and these two are) do not prevent parallel plans.
If you don't want to go the SQLCLR route, then you should, at the very least, keep the ConvertIntToIp function as purely mathematical. There is no reason to do all of those conversions and substrings.
CREATE FUNCTION dbo.IPNumberToAddress(#IPNumber BIGINT)
RETURNS VARCHAR(15)
WITH SCHEMABINDING
AS
BEGIN
DECLARE #Oct1 BIGINT,
#Oct2 INT,
#Oct3 INT;
SET #Oct1 = #IPNumber / (256 * 256 * 256);
SET #IPNumber -= (#Oct1 * (256 * 256 * 256));
SET #Oct2 = #IPNumber / (256 * 256);
SET #IPNumber -= (#Oct2 * (256 * 256));
SET #Oct3 = #IPNumber / 256;
SET #IPNumber -= (#Oct3 * 256);
RETURN CONCAT(#Oct1, '.', #Oct2, '.', #Oct3, '.', #IPNumber);
END;
GO
And then:
SELECT dbo.IPNumberToAddress(3275881995);
-- 195.65.254.11
For the GetCidrIpRange TVF, you would be better off converting that to be an Inline TVF. You can accomplish the multi-step calculations via CTEs in the following manner (you will just need to clean it up a little / finish it):
WITH cte1 AS
(
SELECT 2 AS [Mask] -- replace with real formula
), cte2 AS
(
SELECT 999 AS [Base], -- replace with real formula
POWER(2.0, 32.0 - cte1.[Mask]) - 1 AS [Power],
cte1.[Mask]
FROM cte1
), cte3 AS
(
SELECT SQL#.INET_AddressToNumber(left(#CidrIp, PATINDEX('%/%' , #CidrIp) - 1))
& (cte2.[Base] ^ cte2.[Power]) AS [LowRange],
cte2.[Power],
cte2.[Mask]
FROM cte2
)
SELECT #CidrIp AS [CidrIp],
cte3.[Mask],
cte3.[LowRange],
SQL#.INET_NumberToAddress(cte3.[LowRange]) AS [LowIp],
(cte3.[LowRange] + cte3.[Power]) AS [HighRange],
SQL#.INET_NumberToAddress(cte3.[LowRange] + cte3.[Power]) AS [HighIp],
CONVERT(BIGINT, POWER(2.0, (32.0 - cte3.[Mask]))) AS [AddressQty]
FROM cte3 c;
Related
I am using below function to generate 5 letters random key but it is the combination of all letters and numbers.
But as per requirements I need to generate a random unique key that does not contain the characters 'o' and 'I', and also does not contain the numbers '0' and '1' .
DECLARE #automateKey VARCHAR(15)
DECLARE #Length INT = 6
DECLARE #Count INT = 2
SET #Length = #Length + 1
SET #Count = #Count + 1
SELECT #automateKey =
(SELECT CAST((ABS(Checksum(NewId())) % 10) AS VARCHAR(1)) +
CHAR(ascii('A')+(Abs(Checksum(NewId()))%25)) +
LEFT(newid(),#count) Random_Number)
SELECT (#automateKey)
I am not sure how I can escape the those particular characters and numbers from random key generation
Could anyone please help with this query? Many thanks in advance.
Use RAND() is better than NEWID(), as it is not random in nature.
DECLARE
#Chars varchar(100) = 'ABCDEFGHJKLMNPQRSTUVWXYZ',
#CharsAndNumbers varchar(100) = '23456789ABCDEFGHJKLMNPQRSTUVWXYZ'
SELECT
CONCAT
(
-- Numbers 2-9
FLOOR(RAND() * 8 + 2),
-- Any UPPER CASE character but not I,O
SUBSTRING(#Chars, CONVERT(int, RAND() * LEN(#Chars) + 1), 1),
-- Numbers 2-9 and Any UPPER CASE charcter but not I,O
SUBSTRING(#CharsAndNumbers, CONVERT(int, (RAND() * LEN(#CharsAndNumbers)) + 1), 1),
SUBSTRING(#CharsAndNumbers, CONVERT(int, (RAND() * LEN(#CharsAndNumbers)) + 1), 1),
SUBSTRING(#CharsAndNumbers, CONVERT(int, (RAND() * LEN(#CharsAndNumbers)) + 1), 1)
)
Testing SQL in StackExchange Data Explorer
I would suggest doing something like this:
declare #chars varchar(255);
set #chars = 'ABCDEFGHIJKLMNPQRSTUVWXYZ23456789';
declare #i int;
set #i = 1;
declare #automateKey = varchar(255);
set #automatekey = '';
while #i <= 5
begin
set #automateKey = #automateKey + substring(#chars, cast(rand() * len(#chars) + 1 as int), 1)
set #i = #i + 1;
end;
This solution has two main components. First, all the valid characters are defined as a string. Second, it uses a while loop to set the values using rand().
rand() behaves in a strange way in SQL Server -- it is evaluated only once for a given query when it appears. Hence, I generally do not want to use it in a SELECT statement.
I should add that the following can replace the WHILE loop:
set #automateKey = (substring(#chars, cast(rand() * len(#chars) + 1 as int), 1) +
substring(#chars, cast(rand() * len(#chars) + 1 as int), 1) +
substring(#chars, cast(rand() * len(#chars) + 1 as int), 1) +
substring(#chars, cast(rand() * len(#chars) + 1 as int), 1) +
substring(#chars, cast(rand() * len(#chars) + 1 as int), 1)
);
Your key seems to have 3 parts:
First character is random number 0-10 which is genereted with:
(Abs(Checksum(NewId()))%10)
Second character is random letter which is generated with:
CHAR(ascii('A')+(Abs(Checksum(NewId()))%25))
Third part is three characters that could be either letter or number
LEFT(newid(),#count)
It's not hard to change the first two parts to eliminate unwanted characters, but would require bit of changing to third part.
Instead you can just leave this as is, and add to the end while loop to search and replace unwanted characters - 0 and 1 with random number, O and L with random letter:
DECLARE #automateKey VARCHAR(15)
DECLARE #Length INT = 6
DECLARE #Count INT = 2
SET #Length = #Length + 1
SET #Count = #Count + 1
--this seems unnecessary , why not just SET #Count = 3 ?
SELECT #automateKey =
(SELECT CAST((ABS(Checksum(NewId())) % 10) AS VARCHAR(1)) +
CHAR(ascii('A')+(Abs(Checksum(NewId()))%25)) +
LEFT(newid(),#count) Random_Number)
WHILE (#automateKey LIKE '%0%' OR #automateKey LIKE '%1%')
BEGIN
SELECT #automateKey = REPLACE(#automateKey, '0', (Abs(Checksum(NewId()))%10))
SELECT #automateKey = REPLACE(#automateKey, '1', (Abs(Checksum(NewId()))%10))
END
WHILE (#automateKey LIKE '%O%' OR #automateKey LIKE '%L%')
BEGIN
SELECT #automateKey = REPLACE(#automateKey, 'O', CHAR(ascii('A')+(Abs(Checksum(NewId()))%25)))
SELECT #automateKey = REPLACE(#automateKey, 'L', CHAR(ascii('A')+(Abs(Checksum(NewId()))%25)))
END
SELECT #automateKey
Please consider the following function and function call:
create function [dbo].[GetCidrIpRange2](#CidrIp varchar(15))
returns #result table
(
LowRange varchar(15) not null,
HighRange varchar(15) not null,
AddressQty bigint not null
)
as
begin
declare #Base bigint = cast(4294967295 as bigint)
declare #Mask int = cast(substring(#CidrIp, patindex('%/%' , #CidrIP) + 1, 2) as int)
declare #Power bigint = Power(2.0, 32.0 - #Mask) - 1
declare #LowRange bigint = dbo.[IPAddressToInteger](left(#CidrIp, patindex('%/%' , #CidrIp) - 1)) & (#Base ^ #Power)
declare #HighRange bigint = #LowRange + #Power
insert #result
select
LowRange = #LowRange
,HighRange = #HighRange
,AddressQty = convert(bigint, power(2.0, (32.0 - #Mask)))
return
end
select [dbo].[GetCidrIpRange2]('195.65.254.11/2');
I get the following error:
Cannot find either column "dbo" or the user-defined function or aggregate "dbo.GetCidrIpRange2", or the name is ambiguous.
The name is unique, and when I run only the function, it created the function properly in the session:
Command(s) completed successfully.
What am I doing wrong?
You are missing a FROM:
SELECT * FROM [dbo].[GetCidrIpRange2]('195.65.254.11/2');
I am trying to run this script on SQL Server:
CREATE FUNCTION [dbo].[IPAddressToCidr](#IP AS VARCHAR(15))
RETURNS INT
AS
BEGIN
DECLARE #result INT;
DECLARE #ipInt BIGINT;
SET #ipInt = CONVERT(BIGINT, PARSENAME(#IP, 1)) + CONVERT(BIGINT, PARSENAME(#IP, 2)) * 256 + CONVERT(BIGINT, PARSENAME(#IP, 3)) * 65536 + CONVERT(BIGINT, PARSENAME(#IP, 4)) * 16777216;
SET #result = CAST(( 32 - LOG(4294967296 - #ipInt, 2)) AS INT);
RETURN #result;
END;
The result error is:
Msg 174, Level 15, State 1, Procedure IPAddressToCidr, Line 11
The log function requires 1 argument(s).
I am trying...
SELECT LOG(4294967296,2) /*this has problem*/
SELECT LOG(4294967296) /*this one works but with different results */
Any help with the Log() function with two parameters?
SQL Server 2012+ supports LOG(expr, [base])
With lower version you can use simple Math to get the result:
SELECT LOG(1024)/LOG(2) -- 10, 2 ^ 10 = 1024
SELECT LOG(1000)/LOG(10) -- 3, 10 ^ 3 = 1000
LiveDemo
Change of base formula:
LOG(#arg,base) = LOG(#arg) / LOG(base)
I need to display some data in an SSRS 2008r2 report and the colors have to match a Windows VB app that saves it's colors as integers (e.g.16744703 is a pinkish color). I believe this is ARGB format. I'm not concerned about the alpha value, as the application does not allow the user to modify it.
I'm stuck on the SQL to convert ARGB to something compatible in SSRS. I need to do the translation in SQL as there are other factors that may override an objects color.
I can work with 3 ints for rgb or a hex value
Anyone got any idea how tot do this?
Regards
mark
Figured it out. Here's a function that returs either RGB() or Hex
-- Description: Converts ARGB to RGB(RR,GG,BB)
-- e.g. 16744703 returns RGB(255,128,255) or #FF80FF
CREATE FUNCTION [dbo].[ARGB2RGB]
(
#ARGB AS BIGINT
,#ColorType AS VARCHAR(1) -- 'H' = Hex, 'R' = RGB
)
RETURNS VARCHAR(16)
AS
BEGIN
DECLARE #Octet1 TINYINT
DECLARE #Octet2 TINYINT
DECLARE #Octet3 TINYINT
DECLARE #Octet4 TINYINT
DECLARE #RestOfColor BIGINT
SET #Octet1 = #ARGB / 16777216
SET #RestOfColor = #ARGB - ( #Octet1 * CAST(16777216 AS BIGINT) )
SET #Octet2 = #RestOfColor / 65536
SET #RestOfColor = #RestOfColor - ( #Octet2 * 65536 )
SET #Octet3 = #RestOfColor / 256
SET #Octet4 = #RestOfColor - ( #Octet3 * 256 )
RETURN
CASE #ColorType
WHEN 'R'
THEN 'RGB(' + CONVERT(VARCHAR, #Octet4) + ','
+ CONVERT(VARCHAR, #Octet3) + ',' + CONVERT(VARCHAR, #Octet2)
+ ')'
WHEN 'H'
THEN '#' + RIGHT(sys.fn_varbintohexstr(#Octet4), 2)
+ RIGHT(sys.fn_varbintohexstr(#Octet3), 2)
+ RIGHT(sys.fn_varbintohexstr(#Octet2), 2)
END
END
Hope someone else finds it useful
Regards
Mark
create FUNCTION [dbo].[ConvertRGB]
(
#ARGB AS float
)
RETURNS #ReturnValue TABLE ( R TINYINT,B TINYINT, G TINYINT )
as
BEGIN
DECLARE #testvarbinary binary(4)
DECLARE #strRBG nvarchar(MAX)
set #testvarbinary = CONVERT(binary(4),#ARGB)
set #strRBG=( SELECT substring(sys.fn_varbintohexstr(#testvarbinary),5,6))
DECLARE #temp AS TABLE (hex char(6))
INSERT INTO #temp
VALUES (#strRBG)
DECLARE #strHex AS varchar(16)
SET #strHex = '0123456789abcdef' -- Assuming case-insensitive collation!
INSERT INTO #ReturnValue
( R,G,B )
SELECT 16 * (CHARINDEX(SUBSTRING(hex, 1, 1), #strHex) - 1) + (CHARINDEX(SUBSTRING(hex, 2, 1), #strHex) - 1)
,16 * (CHARINDEX(SUBSTRING(hex, 3, 1), #strHex) - 1) + (CHARINDEX(SUBSTRING(hex, 4, 1), #strHex) - 1)
,16 * (CHARINDEX(SUBSTRING(hex, 5, 1), #strHex) - 1) + (CHARINDEX(SUBSTRING(hex, 6, 1), #strHex) - 1)
FROM #temp
RETURN
END;
GO
--select * from [ConvertRGB](10592513)
I was looking for doing something like this and came up with this post. It was of great help but I found it more appealing doing it inline as follow. Notice that the result can be used inside the SSRS Color Expression with no additional conversions
select CustomerID
,SSRSColor = '#' + SUBSTRING(S.TXTHEXColor, 5, 2)
+ SUBSTRING(S.TXTHEXColor, 3, 2)
+ SUBSTRING(S.TXTHEXColor, 1, 2)
from
(
Select CustomerID
,[TXTHEXColor] = right(sys.fn_varbintohexstr(CONVERT(varbinary, T.Color)), 6)
From SomeTable T
) S
If a function is still needed then following is a much shorter way but bear in mind that with big record sets functions can slow down the process a lot.
CREATE FUNCTION [dbo].[SSRSColor] (
#ARGB AS INT
)
RETURNS VARCHAR(7)
AS
BEGIN
DECLARE #TXTHEXColor varchar(100)
Select #TXTHEXColor = right(sys.fn_varbintohexstr(CONVERT(varbinary, #ARGB)), 6)
return '#' + SUBSTRING(#TXTHEXColor, 5, 2) + SUBSTRING(#TXTHEXColor, 3, 2) + SUBSTRING(#TXTHEXColor, 1, 2)
END
I am doing some performance testing on a SQL sproc and just want to bang out a quick data generator for testing.
I am after a simple way to generate a pseudo random (true random not needed in this case) varchar field.
Ideas I have so far is having a character definition of valid characters that can be used and then build the string from this definition and use a pseudo random length for length variation with a max/min length defined.
Edit:
My test data generator:
DECLARE #MyDataTable TABLE
(
RecID int IDENTITY(1,1) PRIMARY KEY,
SomeText varchar(255)
)
DECLARE #RecId int, #SomeText varchar(255),
#maxlength int, #minlength int,
#RecordCount int, #Counter int
SET #maxlength = 254
SET #minlength = 50
SET #RecordCount = 500000
SET #Counter = 1
WHILE (#Counter < #RecordCount)
BEGIN
INSERT INTO #MyDataTable
(
SomeText
)
SELECT TOP 1
(
select top (abs(checksum(newid())) % (#maxlength-#minlength) + #minlength) char(abs(checksum(newid())) % 26 + ascii('A'))
from sys.all_objects a1
where sign(a1.object_id) = sign(t.object_id) /* Meaningless thing to force correlation */
for xml path('')
) as NewRandomString
FROM sys.all_objects t;
SET #Counter = #Counter + 1
END
I wrote a blog post on this recently.
http://msmvps.com/blogs/robfarley/archive/2009/12/07/randomising-data.aspx
select top (#stringlength) char(abs(checksum(newid())) % 26 + ascii('A'))
from sys.all_objects
for xml path('')
;
Edit: Sorry - didn't include the random length thing...
SELECT
(
select top (abs(checksum(newid())) % (#maxlength-#minlength) + #minlength) char(abs(checksum(newid())) % 26 + ascii('A'))
from sys.all_objects
for xml path('')
) as NewRandomString
FROM yourTable; /* Maybe something like dbo.nums? */
Edit: Sorry - needs to be correlated...
SELECT
(
select top (abs(checksum(newid())) % (#maxlength-#minlength) + #minlength) char(abs(checksum(newid())) % 26 + ascii('A'))
from sys.all_objects a1
where sign(a1.object_id) = sign(t.object_id) /* Meaningless thing to force correlation */
for xml path('')
) as NewRandomString
,*
FROM sys.all_objects t;
For SQL Server 2008
SELECT
--fixed length
CAST(CRYPT_GEN_RANDOM(50) AS varchar(100)),
--variable length
CAST(CRYPT_GEN_RANDOM(ABS(CHECKSUM(NEWID()))%50) AS varchar(100))
Samples:
r¡Ñ”ã8Ò¯wß×1W=ýÎÜTÜN:Læ*é=Öô/qAtmտ׌1):¢ìèð’¾N
mÁBòºÇòWãmßyWßðÛ2ﬔœ¹t ¦2›ÏÀë?î7Ä›››ºªb
My evil twin wants to use this as a password generator...
This will generate a random string of variable length.
DECLARE #text nvarchar(255),
#length int,
#i int;
SET #i = 0
SET #text = ''
SET #length = RAND() * 50 + 215
WHILE (#i < #length)
BEGIN
SET #text = #text + CHAR(RAND() * 26 + 65)
SET #i = #i + 1
END
If you need it quick or you don't want to do it yourself,
you can also use the tool from
http://www.generatedata.com/
but you can only generate 100 rows if you are just using the online demo.