Convert little endian to decimal - sql-server-2016

Is there a way to do an on-the-fly conversion within T-SQL to convert a little Endian (something I only just learned about 20 mins ago) to a decimal value?
In the DB I'm working with, there's a table that gets updated with raw values from devices, all of which are little endian. In this immediate example, I have the value BD 0E 00 3A, and supposedly, this should translate to 3.773 (at least roughly). Is there a function that would allow me to immediately convert this value into my desired decimal value?
EDIT 1: I actually discovered how to translate it by finding one of the developers. It appears that the first two bytes, flipped to big endian, give the 3,773 value I need. When windows calculator is put into programmer mode, selected HEX, feeding it 0EBD gives this result. Does someone know how I might do this same thing using T-SQL?

This is definitely not pretty. I can get you to an integer value of 3773. If that is supposed to translate to 3.773 somehow, it's not clear from the data.
DECLARE #x varchar(32) = 'BD 0E 00 3A';
SELECT CONVERT(int, CONVERT(binary(2),
'0x' + RIGHT(LEFT(REPLACE(#x,' ',''),4),2)
+ LEFT(LEFT(REPLACE(#x,' ',''),4),2),1));
If you know for sure there will always a space between the first two pieces, you can do this slightly simpler:
DECLARE #x varchar(32) = 'BD 0E 00 3A';
SELECT CONVERT(int, CONVERT(binary(2),'0x' + SUBSTRING(#x,4,2) + LEFT(#x,2), 1));
Tell them the next time they want to store some integers in a table, don't get cute or clever about it.

Related

TSQL - Create a GUID from a random text and then get back the original text value from that GUID

I'm curious, is there an efficient way to generate a GUID from a random string of text and then take that GUID and convert it back to the original random string of text without using any additional data/mapping? I looked around for ways to do it, but couldn't find anything substantial.
Take this variable as an example for the starting point. I want to know if I can generate a GUID from it and then destruct the GUID to get back the original #Text
DECLARE #Text CHAR(64) = 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' ;
It seems like you are leaning towards EncryptByPassPhrase() and DecryptByPassPhrase()
Example
declare #encrypt varbinary(200)
select #encrypt = EncryptByPassPhrase('MySecretKey', 'abc' )
select #encrypt
select convert(varchar(100),DecryptByPassPhrase('MySecretKey', #encrypt ))
In SQL Server a UNIQUEIDENTIFIER can be any 16bytes of data. Per:
a string constant in the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx,
in which each x is a hexadecimal digit in the range 0-9 or a-f. For
example, 6F9619FF-8B86-D011-B42D-00C04FC964FF is a valid
uniqueidentifier value.
https://learn.microsoft.com/en-us/sql/t-sql/data-types/uniqueidentifier-transact-sql?view=sql-server-ver15
So any 16 character or less varchar can be round-tripped through a UNIQUEIDENTIFIER. Eg
declare #d varchar(16) = 'hello world'
declare #u uniqueidentifier = cast(cast(#d as varbinary(16)) as uniqueidentifier)
select cast(cast(#u as varbinary(16)) as varchar(16))
To add some general thoughts:
As you were told already, a GUID has a length of 16 bytes.
Assuming you can reduce your string to plain latin lower-case characters you have to deal with 26, together with numbers with 36 different values (not speaking of any dot, comma or question mark etc).
The count of bits reflects the number of possible values. One byte of 8 bit can represent 256 (2^8) different values. For 26 letters you'd need at least 5 bit (2^5=32), together with the numbers you'd have to go up to 6 bit (64 values). The 16-byte GUID represents 128 bits (16x8=128). You could divide this by 5 (~25) or by 6 (~21).
That means: Reduced to 26 plain latin characters you could (with quite some effort) encode up to 25 characters in the memory allocated by one GUID (by using chunks of 5 bits). Together with numbers you are limited to a length of 21.
If you want to deal with any VARCHAR value (which is extended ASCII), you can translate a string to binary and then to GUID and back easily (David Brown showed this in his answer), as long as you limit this to a length of 16 characters.
Otherwise you would need some kind of dictionary on both sides...

SQL Select Converting to ASCII/varchar from Hex

I have data stored in a column in my SQL Server table like the following:
0x003600380032003200330031002D003400
I know the output for this should be:
682231-4
Can someone help me with the SQL code for the conversion?
Something like:
select converttochar (0x003600380032003200330031002D003400)
Returns: 682231-4
I'm not sure if I am looking at hex or binary conversion or what....
It is difficult to see what the exact encoding issue is based on the sample data.
I imagine Larnu is correct and this is just UCS-2 encoded data that for some reason has had a leading null byte added.
Another interpretation is that this is UCS-2 encoded data encoded with a different endianess - so each double byte needs to be reversed (though then there is still a surplus null byte at the end).
As long as each alternate byte is 0x00 it doesn't much matter which case is correct. To strip out the null bytes and cast to varchar you can also use
SELECT REPLACE(0x003600380032003200330031002D003400, 0x00, '' COLLATE Latin1_General_Bin)
For the limited character set shown in the question these will all have the same effect. If the raw data can have non ASCII characters this approach will likely fall down.
This actually looks like you have an nvarchar with a leading 00 at the start. This is messy, but would work:
SELECT CONVERT(nvarchar(30),CONVERT(varbinary(60),STUFF(CONVERT(VARCHAR(60), 0x003600380032003200330031002D003400,1),3,2,''),1));
This strips out the leading 00 characters, making the value 0x3600380032003200330031002D003400, which is the nvarchar value for N'682231-4'.
It strips the value out by converting the varbinary to a varchar but using the style code 1, this means you have a varchar with the value '0x3600380032003200330031002D003400'; allowing you to perform string manipulations. Then I use STUFF to remove the character and CONVERT with style code 1 to make it a varbinary again.
DB<>Fiddle
Alternatively, you could remove all the null character tuples and convert. This looks way messier as you can't just blanket replace '00' (or rather I don't feel it's "safe" to), so I put the characters into their tuples, and rebuild:
DECLARE #YourBinary varbinary(60) = 0x003600380032003200330031002D003400;
WITH N AS(
SELECT N
FROM (VALUES(NULL),(NULL),(NULL),(NULL),(NULL),(NULL))N(N)),
Tally AS(
SELECT TOP(60) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) -1 AS I
FROM N N1, N N2, N N3)
SELECT CONVERT(varchar(60),CONVERT(varbinary(60),(SELECT V.SS + ''
FROM Tally T
CROSS APPLY(VALUES(SUBSTRING(CONVERT(varchar(60),#YourBinary,1),(T.I*2)+1,2)))V(SS)
WHERE V.SS NOT IN ('00','')
FOR XML PATH(''),TYPE).value('.','varchar(60)'),1));
This idea might be better though, if some of your values have a leading 00 and others don't. Of course, this solution also assumes the end value is a varchar, not an nvarchar.
SELECT CONVERT(VARCHAR(60), 0x003600380032003200330031002D003400);
Demo
I had also same issue some ago and the used the below statement for convert and my problem resolved that time ..
SELECT CONVERT(VARCHAR(60), 0x003600380032003200330031002D003400);
Please try to run this and see whether your problem is resolved or not?

Update varbinary(MAX) field in SQLServer 2012 Lost Last 4 bits

Recently I would like to do some data patching, and try to update a column of type varbinary(MAX), the update value is like this:
0xFFD8F...6DC0676
However, after update query run successfully, the value becomes:
0x0FFD8...6DC067
It seems the last 4 bits are lost, or whole value right shifting a byte...
I tried deleting entire row and run an Insert Query, same things happen!
Can anyone tell me why is this happening & how can I solve it? Thanks!
I have tried several varying length of binary, for maximum
43658 characters (Each represents 4 bits, total around 21 KB), the update query runs normally. 1 more character will make the above "bug" appears...
PS1: For a shorter length varbinary as update value, everything is okay
PS2: I can post whole binary string out if it helps, but it is really long and I am not sure if it's suitable to post here
EDITED:
Thanks for any help!
As someone suggested, the value inserted maybe of odd number of 4-bits, so there is a 0 append in front of it. Here is my update information on the value:
The value is of 43677 characters long exluding "0x", which menas Yes, it is odd
It does explain why a '0' is inserted before, but does not explain why the last character disappears...
Then I do an experiment:
I insert a even length value, with me manually add a '0' before the original value,
Now the value to be updated is
0x0FFD8F...6DC0676
which is of 43678 characters long, excluding "0x"
The result is no luck, the updated value is still
0x0FFD8...6DC067
It seems that the binary constant 0xFFD8F...6DC0676 that you used for update contains odd number of hex digits. And the SqlServer added half-byte at the beginning of the pattern so that it represent whole number of bytes.
You can see the same effect running the following simple query:
select 0x1, 0x104
This will return 0x01 and 0x0104.
The truncation may be due to some limitaions in SSMS, that can be observed in the following experiment:
declare #b varbinary(max)
set #b = 0x123456789ABCDEF0
set #b = convert(varbinary(max), replicate(#b, 65536/datalength(#b)))
select datalength(#b) DataLength, #b Data
The results returned are 65536 and 0x123456789ABCDEF0...EF0123456789ABCD, however if in SSMS I copy Data column I'm getting pattern of 43677 characters length (this is without leading 0x), which is 21838.5 bytes effectively. So it seems you should not (if you do) rely on long binary data values obtained via copy/paste in SSMS.
The reliable alternative can be using intermediate variable:
declare #data varbinary(max)
select #data = DataXXX from Table_XXX where ID = XXX
update Table_YYY set DataYYY = #data where ID = YYY

Use max in a VARCHAR to get result > 999999?

What if somebody made a column as VARCHAR2(256 CHAR) and there are only numbers in this column. I would like to get the highest number. The problem is: the number is something > 999999 but a Max to a varchar is always giving me a max number of 999999
I tried to_number(max(numbers), '9999999999999') but i still get 999999 back, at that cant be. Any ideas? Thank you
the best way is to
First Solution
convert the column in numeric
or
Second Solution
convert data in you query in numeric and than get data...
Example
select max(col1) from(
select to_number(numbers) as col1 from table ) d
It has to be this way because if you call MAX() before TO_NUMBER(), it will sort alphabetically, and then 999999 is bigger than 100000000000. Note that applying TO_NUMBER() to a varchar2 column incurs the risk of an INVALID_NUMBER exception, should the column containing any non-numeric characters. This is why the first proposed solution is to be preferred.
In Oracle, the NUMBER type contains base 100 floating point values which have a precision of 38 significant digits, and a max value of 9999...(38 9's) x 10^125. There are two questions at issue - the first is whether a NUMBER can contain a value converted from a 256 character string, and the second is if two such values which are 'close' in numeric terms can be distinguished.
Let's start with taking a 256 character string and trying to convert it to a number. The obvious thing to do is:
SELECT TO_NUMBER('9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999') AS VAL
FROM DUAL;
Executing the above we get:
ORA-01426: numeric overflow
which, having paid attention earlier, we expected. The largest exponent that a NUMBER can handle is 125 - and here we're trying to convert a value with 256 significant digits. NUMBER's can't handle this. If we cut the number of digits down to 125, as follows:
SELECT TO_NUMBER('99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999') AS VAL
FROM DUAL;
It works fine, and our answer it 1E125.
<blink>
WHOA! WAIT!! WHAT??? The answer is 1 x 10^125??? What about all those 9's?!?!?!?
Remember earlier I'd mentioned that an Oracle NUMBER is a floating point value with a maximum precision of 38 and a maximum exponent of 125. From the point of view of TO_NUMBER 125 9's all strung together can't be exactly represented - too many digits (remember, max. precision of 38 (more on this later)). So it does the absolute best it can - it converts the first 38 digits (all of which are 9's) and then says "How should I best round this off to make the result A) representative of the input and B) as close as I can get to what I was given?". In this case it looks at digit 39, sees that it's a 9, and decides to round upward. As all the other digits are also 9's, it continues rounding neatly until it ends up with 1 as the remaining mantissa digit.
* Later, back at the ranch... *
OK, earlier I'd mentioned that NUMBER has a precision of 38 digits. That's not entirely true - it can actually differentiate between values with up to 40 digits of precision, at least sometimes, if the wind is right, and you're going downhill. Here's an example:
SELECT CASE
WHEN to_number('9999999999999999999999999999999999999999') >
to_number('9999999999999999999999999999999999999998')
THEN 'Greater'
ELSE 'Not greater'
END AS VAL
FROM DUAL;
Those two values each have 40 digits (counting is left as an exercise to the extremely bored reader :-). If you execute the above you'll get back 'Greater', showing that the comparison of two 40 digit values succeeded.
Now for some fun. If you add an additional '9' to each string, making for a 41 digit value, and re-execute the statement it'll return 'Not greater'.
<blink>
WAIT! WHAT?? WHOA!!! Those values are obviously different! Even a TotalFool (tm) can see that!!
The problem here is that a 41 digit number exceeds the precision of the NUMBER type, and thus when TO_NUMBER finds it has a value this long it starts discarding digits on the right side. Thus, even though those two really big numbers are clearly different to you and me, they're not different at all once they've been folded, spindled, mutilated, and converted.
So, what are the takeaways here?
1 - To the OP's original question - you'll have to come up with another way to compare your number strings besides using NUMBER because Oracle's NUMBER type can't hold 256 digit values. I suggest that you normalize the strings by making sure ALL the values are 256 digits long, adding zeroes on the left as needed, and then a string comparison should work OK.
2 - Floating point numbers prove the existence of (your favorite deity/deities here) by negation, as they are clearly the work of (your favorite personification of evil here). Whenever you work with them (as we all have to, sooner or later) you should remember that they are the foul byproducts of malignant evil, waiting to lash out at you when you least expect it.
3 - There is NO point three! (And extra credit for those who can identify without resorting to an extra-cranial search engine where this comes from :-)
Share and enjoy.
If you mean that the numbers in the column can be that big (256 digits), you could try something like this:
SELECT numbers
FROM (
SELECT numbers
FROM table_name
ORDER BY LPAD(numbers, 256) DESC
)
WHERE rownum = 1
or like this:
SELECT LTRIM(MAX(LPAD(numbers, 256))) AS numbers
FROM table_name

Good way to format decimal in SQL Server

We store a decimal(9,8) in our database. It can have any number of places after the decimal point (well, no more than 8). I am frustrated because I want to display it as human-readable text as part of a larger string created on the server. I want as many decimals to the right of the decimal point as are non-zero, for example:
0.05
0.12345
3.14159265
Are all good
If I do
CAST(d AS varchar(50))
I get formatting like:
0.05000000
0.12345000
3.14159265
I get similar output if I cast/convert to a float or other type before casting to a varchar. I know how to do a fixed number of decimal places, such as:
0.050
0.123
3.142
But that is not what I want.
Yes, I know I can do this through complicated string manipulation (REPLACE, etc), there should be a good way to do it.
Playing around (sql server) i find that casting to float first makes the trick ..
select cast( cast(0.0501000 as float) as varchar(50) )
yields
0.0501
Code copied almost verbatim from here (also discusses the 6-digit limit on float formatting in mode 0):
DECLARE #num3 TABLE (i decimal(9, 8))
INSERT #num3
SELECT 0.05
UNION ALL
SELECT 0.12345
UNION ALL
SELECT 3.14159265
SELECT i
,CASE WHEN PATINDEX('%[1-9]%', REVERSE(i)) < PATINDEX('%.%', REVERSE(i))
THEN LEFT(i, LEN(i) - PATINDEX('%[1-9]%', REVERSE(i)) + 1)
ELSE LEFT(i, LEN(i) - PATINDEX('%.%', REVERSE(i)))
END 'Converted'
FROM #num3
For anything other than fairly straight forward manipulation, I'd be considering doing this in your calling code instead tbh as I think it's usually best for SQL to return the data as-is from the database, and then leave the formatting of that up to whatever is calling it, which is more than likely better geared up for string manipulation. Especially if you find yourself jumping though hoops to try to achieve it.