Set bit in a varbinary - sql-server

I have a varbinary column and a bit number which can be bigger than 32. How can I set/test a bit having it's number in the varbinary column?
I tried to do
convert(varbinary(max), power(2, #bit_number))
to get me varbinary, but it is limited to float size numbers.
There is an extended stored procedure which can do OR of two varbinaries in our DB but I need to pass this long varbinary mask, or maybe there is a way to update some byte in varbinary knowing it's number?

You can always convert the varbinary to a string, manipulate it, and convert it back. For example, try this:
DECLARE #Foo varbinary(max)
SELECT #Foo = 0x0123456789abcdef0123456789abcdef
DECLARE #Temp varchar(max)
SELECT #Temp = CONVERT(varchar(max), #Foo, 1)
DECLARE #ByteOffset integer;
SELECT #ByteOffset = 3 --offset into varbinary to access
DECLARE #GetValue binary(1)
SELECT #GetValue = CONVERT(binary(1), '0x' + SUBSTRING(#Temp, (#ByteOffset * 2) + 3, 2),1 )
PRINT #GetValue -- fetch the value at #ByteOffset
DECLARE #SetValue binary(1)
SELECT #SetValue = 0x04 --value to modify
DECLARE #Foo2 varbinary(max)
SELECT #Foo2 = CONVERT(varbinary(max), SUBSTRING(#Temp, 1, (#ByteOffset * 2) + 2) + SUBSTRING(CONVERT(varchar, #SetValue, 1), 3, 2) + SUBSTRING(#Temp, (#ByteOffset * 2) + 5, LEN(#Temp)), 1)
PRINT #Foo2 --contains the new varbinary with #ByteOffset updated with #SetValue

Related

SQL Server - error converting data type nvarchar to float

I am inserting table A to table B. The problematic column looks like -$25.2. I first replaced the $ and tried insert. Got this error
Error converting data type nvarchar to float.
I then checked by
SELECT *
FROM B
WHERE ISNUMERIC([Col Name]) <> 1
and no results were returned.
This is odd. It is supposed to return something.
What should I check next?
I also tried something like
CAST(REPLACE([Col Name], '-$', '') AS FLOAT)
Try using this
DECLARE #Text nvarchar(100)
SET #Text = '-$1234.567'
SET #Text = Replace(#Text,'$', '')
Select CONVERT(float, #Text) AS ColumnValue
Select ABS(CONVERT(float, #Text)) AS ColumnValue
While the 'money' data type isn't great for doing calculations, in this scenario you can use it as an intermediary.
declare #a nvarchar(10)
set #a = '-$25.2'
select
#a,
cast(cast(#a as money) as float)
Only use this though if your data only goes to a max of 4 decimal places, otherwise you will lose precision in the conversion.

How to get the division of 2 integers not to return 0

why SET #H1 = CAST((#rCount / #poolTot) AS VARCHAR(50)) + '%' keeps returning 0%?
#H1 is a VARCHAR(50) while #rCount and #poolTot are integers with values of 8 and 10. I should get something like .80%
Thanks for helping
Either your dividend or divisor (or both) need to be a datatype with decimal places (decimal, float, etc). One way of doing this is to muliply one of the numbers by 1.0, to force the conversion. Or you can do an explicit conversion.
Your results may vary, depending on the data type you choose. Using the first method below, you will get "0.80000000000000%". If you cast it cast #poolTot as a float, like the second example, you will get "0.8%". It really depends on what you want your results to look like.
DECLARE #H1 VARCHAR(50)
,#rCount INT = 8
,#poolTot INT = 10
SET #H1 = CAST(((#rCount)/ (#poolTot * 1.0)) AS VARCHAR(50)) + '%'
SELECT #H1
_
DECLARE #H1 VARCHAR(50)
,#rCount INT = 8
,#poolTot INT = 10
SET #H1 = CAST(((#rCount)/ cast(#poolTot as float)) AS VARCHAR(50)) + '%'
SELECT #H1
If you want to convert the result in decimal you can do something like
Declare #rCount as int=8;
declare #poolTot as int =10
select CAST((CONVERT(DECIMAL(10,2), #rCount)) /
(CONVERT(DECIMAL(10,2), #poolTot))as varchar(50)) + '%'
Output will be like
Again if you want only two digit after decimal you can do something like
Declare #rCount as int=8;
declare #poolTot as int =10
declare #Temp as decimal(10,2)=(CONVERT(DECIMAL(10,2), #rCount)) /
(CONVERT(DECIMAL(10,2), #poolTot))
select convert(varchar(10),#Temp,2) + '%'
Than it will display like
I think you can use this:
SET #H1 = CAST((100 * #rCount / #poolTot) AS VARCHAR(50)) + '%'

SQL server convert hex string to varbinary

I have a string column that represents hex values, for example -
'274', '1A7', '3D1' and so on.
Now I need to convert these values to their integer values, so that '10' will be converted to 16, for example.
The code I use:
SELECT CONVERT(int, CONVERT(varbinary, '0x' + case when replicate('0', len(myHex) / 2) + myHex = '0' then '00' else replicate('0', len(myHex) / 2) + myHex end, 1))
I'm actually padding the string with a zero or two to make it's length even, and adding the '0x' prefix. However some (random) rows fail.
Is there another way to convert the values?
Thanks.
please give feedback
so that i can improve my answer
Here is one way to do it:
//create function fn_HexToIntnt(#str varchar(16))
//returns bigint as begin
select #str=upper(#str)
declare #i int, #len int, #char char(1), #output bigint
select #len=len(#str)
,#i=#len
,#output=case
when #len>0
then 0
end
while (#i>0)
begin
select #char=substring(#str,#i,1), #output=#output
+(ASCII(#char)
-(case
when #char between ‘A’ and ‘F’
then 55
else
case
when #char between ’0′ and ’9′
then 48 end
end))
*power(16.,#len-#i)
,#i=#i-1
end
return #output
end
or
SELECT CONVERT(INT, 0×00000100)
SELECT CONVERT(VARBINARY(8), 256)

Convert Hexadecimal to INT and vice versa

I will be creating a sequential Serial Number made from Hexadecimal values
With this Format:
XX-XX-XX-YYYY
Which XX-XX-XX is default value
And YYYY is the incrementing hexa decimal value
Now to create the serial number based on hex value I need Add 6 to the last generated hex value
MIN: 2D41 + 6 = 2D47
2D47 + 6 ... and so on
MAX: 4100 generation of serial will stop when I meet the MAX value.
I already created it in c# but I need to do it on SQL
int num1 = int.Parse("2D41", NumberStyles.HexNumber); //Convert hex to int
int result = num1 + 6; //Add + 6 for increment
string myHex = result.ToString("X"); //Convert result to hex
MessageBox.Show(myHex); // result 2D47
How can this be done in T-SQL?
DECLARE #x VARBINARY(8) = 0x00002D41;
SELECT CONVERT(VARBINARY(8), CONVERT(INT, #x) + 6);
In order to handle the output as a string:
DECLARE #x VARBINARY(8) = 0x00002D41;
SELECT CONVERT(CHAR(10), CONVERT(VARBINARY(8), CONVERT(INT, #x) + 6), 1);
Hope this helps you
declare #seed varchar(max) = '2D41';
declare #limit varchar(max) = '4100';
select convert(int, convert(varbinary(max), '0x'+#seed,1)),
convert(int, convert(varbinary(max), '0x'+#limit,1));
;with seedlimit(seed, limit) as (
select convert(int, convert(varbinary(max), '0x'+#seed,1)),
convert(int, convert(varbinary(max), '0x'+#limit,1))
)
select SerialNumber = 'XX-XX-XX-' + right(convert(varchar(10),cast(s.seed + 6 * v.number as varbinary(max)),1),4)
from seedlimit s
join master.dbo.spt_values v on type='p'
where s.seed + 6 * v.number <= s.limit;
The basic ingredients are in there for you to create a view/procedure/function out of the answer,
Output:
SerialNumber
-------------
XX-XX-XX-2D41
XX-XX-XX-2D47
...
XX-XX-XX-40F7
XX-XX-XX-40FD
If you already have it in C#, leave it there and simply convert your code to a SQL CLR function.
For a simple example see:
http://blog.sqlauthority.com/2008/10/19/sql-server-introduction-to-clr-simple-example-of-clr-stored-procedure/

Increment a uniqueidentifier in TSQL

I am looking for a way to increment a uniqueidentifier by 1 in TSQL. For example, if the id is A6BC60AD-A4D9-46F4-A7D3-98B2A7237A9E, I'd like to be able to select A6BC60AD-A4D9-46F4-A7D3-98B2A7237A9F.
#rein It's for a data import. We have an intermediate table with IDs that we're generating records from, and we join on those IDs later in the import. Unfortunately, now some of those records generate a couple of records in the next table, so we need a new id that is reproducible.
The way you want to increment Guid is not correct for SQL Server as Guid is a structure with different byte order in the byte groups, please have a look at:
http://sqlblog.com/blogs/alberto_ferrari/archive/2007/08/31/how-are-guids-sorted-by-sql-server.aspx
and notice the following:
Now, when I run modified Alberto's query, I'm getting the following sequence:
3, 2, 1, 0, 5, 4, 7, 6, 9, 8, 15, 14, 13, 12, 11, 10
That means, that GUID's byte #3 is the least significant and GUID's byte #10 is the most significant [from SQL Server ORDER BY clause perspective].
Here is simple function to increment a uniqueidentifier accounting for this:
create function [dbo].[IncrementGuid](#guid uniqueidentifier)
returns uniqueidentifier
as
begin
declare #guid_binary binary(16), #b03 binary(4), #b45 binary(2), #b67 binary(2), #b89 binary(2), #bAF binary(6)
select #guid_binary = #guid
select #b03 = convert(binary(4), reverse(substring(#guid_binary,1,4)))
select #b45 = convert(binary(2), reverse(substring(#guid_binary,5,2)))
select #b67 = convert(binary(2), reverse(substring(#guid_binary,7,2)))
select #b89 = convert(binary(2), substring(#guid_binary,9,2))
select #bAF = convert(binary(6), substring(#guid_binary,11,6))
if (#b03 < 'FFFFFFFF')
begin
select #b03 = convert(binary(4), cast(#b03 as int) + 1)
end
else if (#b45 < 'FFFF')
begin
select #b45 = convert(binary(2), cast(#b45 as int) + 1)
end
else if (#b89 < 'FFFF')
begin
select #b89 = convert(binary(2), cast(#b89 as int) + 1)
end
else
begin
select #bAF = convert(binary(6), cast(#bAF as bigint) + 1)
end
return convert(binary(16), reverse(convert(char(4),#b03)) + reverse(convert(char(2),#b45)) + reverse(convert(char(2),#b67)) + convert(char(2),#b89) + convert(char(6),#bAF))
end
Note that bytes 6 and 7 are not incremented as they contain the Guid version bits.
But as others has pointed you really should not be doing this. In your case it might be better if you create a temp table for these Guids (with two columns: one integer as index and second one with generated Guids).
Here is one way I've come up with, but I'm hoping there is a better way.
LEFT([ID], 19) + RIGHT(CONVERT(uniqueidentifier, CONVERT(binary(16), CONVERT(binary(16), [ID]) + CONVERT(bigint, 1))), 17) AS 'MyNewID'
You can do this approach, but I'm not accounting for the case of overflowing lower 8 bytes.
declare #guid uniqueidentifier, #binaryUpper8 binary(8), #binaryLower8 binary(8), #binary16 binary(16), #bigint bigint
set #guid = 'A6BC60AD-A4D9-46F4-A7D3-98B2A7237A9E'
set #binary16 = cast(#guid as binary(16))
--harvest lower 8 bytes
select #binaryUpper8= substring(#binary16, 1, 8)
,#binaryLower8 = substring(#binary16, 9, 8)
set #bigint = cast(#binaryLower8 as bigint)
--increment
set #bigint = #bigint + 1
--convert back
set #binaryLower8 = cast(#bigint as binary(8))
set #binary16 = #binaryUpper8 + #binaryLower8
set #guid = cast(#binary16 as uniqueidentifier)
select #guid

Resources