SQL byte truncate - sql

My input is a varchar payload (that is actually made of hexadecimal characters). I would like to convert it to bits, then truncate the result to decode it.
I already have a built-in function (hexstrtovarbin) that works correctly and that converts varchars into varbinaries.
For example, for an input "4d", I would like to convert it into bits (01001101) then truncate the first 6 digits, before converting them into an integer (and get eventually 19).
DECLARE #payload varchar(4), #binarypayload binary(1), #converted smallint;
SET #payload = '4d';
SET #binarypayload = hexstrtovarbin(#payload);
SET #converted = CAST(SUBSTRING(#binarypayload, 1, 6) AS int)
If I proceed like this, #converted takes 77 as value. This is because #binarypayload value is "TQ==" (and not actual bits), so the substring does not truncate it.
I have tried to use bit data type, but could not store more than 1 of them.
Would anyone know how to get the actual bits in order to truncate them?

it can be messy but this is my answer, according to your data in the question:
declare #intvalue int
set #intvalue= CONVERT(int, CONVERT(varbinary(max), '4d', 2) )
declare #vsresult varchar(16)
declare #inti int
select #inti = 16, #vsresult = ''
declare #Input varchar(16)
--translating ex string in binary digits
while #inti>0
begin
select #vsresult=convert(char(1), #intvalue % 2)+#vsresult
select #intvalue = convert(int, (#intvalue / 2)), #inti=#inti-1
end
set #Input= left(#vsresult,LEN(#vsresult)-2) -- here your input string without last two digits, 00000000010011
--now return integer value
DECLARE #Cnt tinyint = 1
DECLARE #Len tinyint = LEN(#Input)
DECLARE #Output bigint = CAST(SUBSTRING(#Input, #Len, 1) AS bigint)
WHILE(#Cnt < #Len) BEGIN
SET #Output = #Output + POWER(CAST(SUBSTRING(#Input, #Len - #Cnt, 1) * 2 AS bigint), #Cnt)
SET #Cnt = #Cnt + 1
END
select #Output

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

Convert from bigint to varchar without leading 0?

I need to use a bigint value as part of a string construction, but I can't figure out how to get from bigint to to varchar without having a leading 0
declare #a bigint = 167830720612159876
select convert(varchar(32), convert(varbinary, #a), 2)
This gives me 02544126B47C5184, but I want `2544126B47C5184'
Basically I want the conversion from bigint to varbinary to omit the leading 0 so that it is left out of the string representation.
Try this:
create function dbo.BigintToHex(#X bigint)
returns varchar(20) as
begin
declare #H varchar(20) = ''
declare #D varchar(16) = '0123456789ABCDEF'
if #X = 0
set #H = '0'
if #X < 0
set #H = '-' + dbo.BigintToHex(-#X)
else
while #X > 0
begin
set #H = SUBSTRING(#D,#X % 16+1,1) + #H
set #X = #X / 16
end
return #H
end
go
select dbo.BigintToHex(-167830720612159876)

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

SQL 2008r2 Hex 2 ascii issue

I have an issue converting the following hex string to ascii in tsql:
'd520088000000000000000004000000200000000000003770001c7cc5353482d322e302d4f70656e5353485f352e3570312044656269616e2d347562756e7475340d0a0000034c0614403d6544c243e7535320dc7ab5c3c8de0000007e6469666669652d68656c6c6d616e2d67726f75702d65786368616e67652d7368613235362c6469666669652d68656c6c6d616e2d67726f75702d65786368616e67652d736861312c6469666669652d68656c6c6d616e2d67726f757031342d736861312c6469666669652d68656c6c6d616e2d67726f7570312d73686131000000497373682d7273612d636572742d763030406f70656e7373682e636f6d2c7373682d6473732d636572742d763030406f70656e7373682e636f6d2c7373682d7273612c7373682d6473730000009d6165733132382d6374722c6165733139322d6374722c6165733235362d6374722c617263666f75723235362c617263666f75723132382c6165733132382d6362632c336465732d6362632c626c6f77666973682d6362632c636173743132382d6362632c6165733139322d6362632c6165733235362d6362632c617263666f75722c72696a6e6461656c2d636263406c797361746f722e6c69752e73650000009d6165733132382d6374722c6165733139322d6374722c6165733235362d6374722c617263666f75723235362c617263666f75723132382c6165733132382d6362632c336465732d6362632c626c6f77666973682d6362632c636173743132382d6362632c6165733139322d6362632c6165733235362d6362632c617263666f75722c72696a6e6461656c2d636263406c797361746f722e6c69752e736500000069686d61632d6d64352c686d61632d736861312c756d61632d3634406f70656e7373682e636f6d2c686d61632d726970656d643136302c686d61632d726970656d64313630406f70656e7373682e636f6d2c686d61632d736861312d39362c686d61632d6d64352d393600000069686d61632d6d64352c686d61632d736861312c756d61632d3634406f70656e7373682e636f6d2c686d61632d726970656d643136302c686d61632d726970656d64313630406f70656e7373682e636f6d2c686d61632d736861312d39362c686d61632d6d64352d39360000001a6e6f6e652c7a6c6962406f70656e7373682e636f6d2c7a6c69620000001a6e6f6e652c7a6c6962406f70656e7373682e636f6d2c7a6c69620000000000000000000000000000000000000000'
It should convert to
'? ??????????#??????????w????SSH-2.0-OpenSSH_5.5p1
Debian-4ubuntu4?????L??#=eD?C?SS
?z???????~diffie-hellman-group-exchange-sha256,diffie-hellman-group-exchange-sha1,diffie-hellman-group14-sha1,diffie-hellman-group1-sha1???Issh-rsa-cert-v00#openssh.com,ssh-dss-cert-v00#openssh.com,ssh-rsa,ssh-dss????aes128-ctr,aes192-ctr,aes256-ctr,arcfour256,arcfour128,aes128-cbc,3des-cbc,blowfish-cbc,cast128-cbc,aes192-cbc,aes256-cbc,arcfour,rijndael-cbc#lysator.liu.se????aes128-ctr,aes192-ctr,aes256-ctr,arcfour256,arcfour128,aes128-cbc,3des-cbc,blowfish-cbc,cast128-cbc,aes192-cbc,aes256-cbc,arcfour,rijndael-cbc#lysator.liu.se???ihmac-md5,hmac-sha1,umac-64#openssh.com,hmac-ripemd160,hmac-ripemd160#openssh.com,hmac-sha1-96,hmac-md5-96???ihmac-md5,hmac-sha1,umac-64#openssh.com,hmac-ripemd160,hmac-ripemd160#openssh.com,hmac-sha1-96,hmac-md5-96????none,zlib#openssh.com,zlib????none,zlib#openssh.com,zlib????????????????????'
Any ideas? I tried using cast and convert but no success.
Any help is much appreciated.
I am using the below function no outside code - tables are populated by an external application
*
declare #hexstring VARCHAR(8000))
RETURNS VARCHAR(8000)
AS
begin
declare #char1 char(1), #char2 char(1), #strlen int, #currpos int, #result varchar(8000)
set #strlen=len(#hexstring)
set #currpos=1
set #result=''
while #currpos<#strlen
begin
set #char1=substring(#hexstring,#currpos,1)
set #char2=substring(#hexstring,#currpos+1,1)
if (#char1 between '0' and '9' or #char1 between 'A' and 'F')
and (#char2 between '0' and '9' or #char2 between 'A' and 'F')
set #result=#result+
char((ascii(#char1)-case when #char1 between '0' and '9' then 48 else 55 end)*16+
ascii(#char2)-case when #char2 between '0' and '9' then 48 else 55 end)
set #currpos = #currpos+2
end
return #result
end
GO
*
Here is a SQL function that will convert a hex string:
BEGIN
DECLARE #hexstring AS VARCHAR(8000)
SET #hexstring = '4368726973204Ce4747461'
DECLARE #strlen AS INT;
SET #strlen = Len(#hexstring)
DECLARE #currpos AS INT
SET #currpos = 1
DECLARE #hexpos AS VARCHAR(16)
SET #hexpos = '0123456789abcdef'
DECLARE #result AS VARCHAR(8000)
SET #result = ''
DECLARE #ch AS INT
WHILE #currpos < #strlen
BEGIN
SET #ch = CONVERT( INT, 16 * (CHARINDEX( SUBSTRING( #hexstring, #currpos, 1), #hexpos, 1) - 1)
+ (CHARINDEX(SUBSTRING(#hexstring, #currpos+1, 1), #hexpos, 1) - 1))
SET #result = #result + CASE WHEN #ch >= 32 AND #ch < 128 THEN CHAR(#ch) ELSE '?' END
SET #currpos = #currpos + 2
END
SELECT #result
END
I added a short hex string to show the function works.
Your hex string didn't convert so well. It may be because of the nulls in it, so I added the #char >= 32 part to strip out control codes (only convert printable ASCII characters, otherwise insert a ?) and you get a string that looks like the one you are after.
The conversion can be done with a single statement. Since you were using a function I provided my answer as such. Note that this solution requires that the input #hexstring is not prefixed with 0x.
CREATE FUNCTION [dbo].[HexToAscii]
(
#hexstring VARCHAR(MAX)
)
RETURNS VARCHAR(MAX)
AS
BEGIN
RETURN CONVERT(VARCHAR(MAX), CONVERT(VARBINARY(MAX), #hexstring, 2))
END

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.