querying binary column using like in sql server - sql-server

I'm using SQL Server 2008. In my table I have a column called TestData of type binary.
Sample data in TestData column are
1. 0x0001DC780C0030373156635D0C00B8840301009A0600AC
2. 0x0301DC780C0030373156385D0C006499C401009A0600AC
Wrote below two queries to get the rows where TestData starts with "0x0001". But none of them are working.
SELECT *
FROM T_TRANSACTION
WHERE CAST(Indicium AS nvarchar(MAX)) LIKE '0x0001%'
----No results found
SELECT *
FROM T_TRANSACTION
WHERE CAST(Indicium AS nvarchar(MAX)) LIKE '0x0001%'
----Returns all the rows
Please correct the query to get the expected results

Don't convert it, but treat is as a range (like you would datetime values)
DECLARE #foo TABLE (TestData varbinary(100) NOT NULL);
INSERT #foo (TestData) VALUES
(0x0001DC780C0030373156635D0C00B8840301009A0600AC),
(0x0001AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA),
(0x0001AFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF),
(0x0301DC780C0030373156385D0C006499C401009A0600AC),
(0x0301FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF),
(0x0302000000000000000000000000000000000000000000);
SELECT *
FROM #foo
WHERE TestData >= 0x0001 AND TestData < 0x0002;
-- added more digit for clarity of what actually happens
SELECT *
FROM #foo
WHERE TestData >= 0x00010000 AND TestData < 0x00020000;
SELECT *
FROM #foo
WHERE TestData >= 0x0001AA AND TestData < 0x0001AB;
SELECT *
FROM #foo
WHERE TestData >= 0x0301 AND TestData < 0x0302;
This has the bonus of being able to use an index on TestData
Edit, you just specify as many digits as you need

For a leading prefix LIKE comparison, gbn's answer will do. For a real LIKE equivalence of string searches, you can use LIKE as follows:
(borrowing schema and sample data from #gbn)
DECLARE #foo TABLE (TestData varbinary(100) NOT NULL);
INSERT #foo (TestData) VALUES
(0x0001DC780C0030373156635D0C00B8),
(0x0001AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA),
(0x0001AFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF),
(0x0301DC780C0030373156385D0C006499C401009A0600AC),
(0x0301FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF),
(0x0302000000000000000000000000000000000000000000);
SELECT *
FROM #foo
WHERE CAST(TestData AS VARCHAR(MAX)) LIKE '%'+CAST(0xDC78 AS VARCHAR(MAX))+'%';
When you cast a binary value to VARCHAR, all it does is treat the raw bits as a string stream. It does not magically convert it into the string representation. Consider the example below:
select cast(0x41 as varchar(10)); -- Result: A
select cast(0x414263 as varchar(10)); -- Result: ABc
Because the byte 0x41 or ordinal 65 is 'A' in the standard Latin codepage.

Don't use the byte array as string, use it like a number.
all you need to do is:
SELECT * FROM T_TRANSACTION WHERE Indicium >= 0x0001
or if you want to get a scpecific one:
SELECT * FROM T_TRANSACTION WHERE Indicium >=0x0001DC780C0030373156635D0C00B8840301009A0600AC

Related

How to reduce one number from string data

How to less numeric value from data type varchar.
For example, I have table which contains value like '2019-02-15', '2019-02-20 10:00:15.000', '3476', '3478956', and I need to less -1 from all of them. How can i do that.
Below is sample query
CREATE TABLE dbo.TestTable (SampleData VARCHAR(100))
INSERT INTO dbo.TestTable ([SampleData])
VALUES ('2019-02-15')
,('2019-02-20 10:00:15.000')
,('3476')
,('3478956')
SELECT * FROM dbo.TestTable
If 2012+, you can use try_convert() within a case statement
Example
SELECT *
,RequiredData = case when try_convert(int ,SampleData) is not null then convert(varchar(100),try_convert(int,SampleData)-1)
when try_convert(date,SampleData) is not null then convert(varchar(100),dateadd(DAY,-1,try_convert(date,SampleData)) )
end
FROM dbo.TestTable
Returns
SampleData RequiredData
2019-02-15 2019-02-14
2019-02-20 10:00:15.000 2019-02-19
3476 3475
3478956 3478955

How to split a varbinary(max) into a list of ints? (and the other way)

I'm curently storing a list of ids in a column as a CSV string value ('1;2;3').
I'd like to optimize with a better approach (I believe) which would use varbinary(max).
I'm looking for tsql functions
1 . That would merge side by side a set of integer rows into a varbinary(max)
2 . That would split the varbinary field into a set of int rows
Any tips appreciated, thanks
Solution is very questionable. I'd also suggest to normalize the data.
However, if you still want to store your data as VARBINARY, here is the solution:
CREATE FUNCTION dbo.fn_String_to_Varbinary(#Input VARCHAR(MAX))
RETURNS VARBINARY(MAX) AS
BEGIN
DECLARE #VB VARBINARY(MAX);
WITH CTE as (
SELECT CAST(CAST(LEFT(IT,CHARINDEX(';',IT)-1) AS INT) as VARBINARY(MAX)) as VB, RIGHT(IT,LEN(IT) - CHARINDEX(';',IT)) AS IT
FROM (VALUES (#Input)) as X(IT) union all
SELECT VB + CAST(CAST(LEFT(IT,CHARINDEX(';',IT)-1) AS INT) as VARBINARY(MAX)) as VB, RIGHT(IT,LEN(IT) - CHARINDEX(';',IT)) AS IT FROM CTE WHERE LEN(IT) > 1
)
SELECT TOP 1 #VB = VB FROM CTE
ORDER BY LEN(VB) DESC
RETURN #VB
END
GO
DECLARE #Input VARCHAR(MAX) = '421;1;2;3;5000;576;842;375;34654322;18;67;000001;1232142334;'
DECLARE #Position INT = 9
DECLARE #VB VARBINARY(MAX)
SELECT #VB = dbo.fn_String_to_Varbinary(#Input)
SELECT #VB, CAST(SUBSTRING(#VB,4*(#Position-1)+1,4) AS INT)
GO
The function converts string into VARBINARY and then script extracts 9th number from that VARBINARY value.
Do not run this function against a data set with million records and million numbers in each line.

How do you write a SQL query to find all the rows that has a float currency value like $15.34 in a nvarchar field

How do you write a SQL query to find only the rows that has a float currency value like $15.34 and NOT round currency value like 15 in a nvarchar field.
Assuming you have a mix of numeric and non-numeric, this should work to return all decimal values that are not whole dollar amounts:
Select * from tablename
where colname like '%.%' --Has a decimal (as in original query)
and colname not like '%.00' --Does not end with 00
It is as simple as
Select * from tablename where columnname = '15.34'
I would strip the $ out, and check if it evaluates to a numeric or not, and use a modulo to be sure a remainder remains when divided by 1.
DECLARE #TEST TABLE (columnname NVARCHAR(15))
INSERT INTO #TEST
SELECT '$15.34' UNION
SELECT 'ZERO' UNION
SELECT '$123.00'
SELECT *, CONVERT(MONEY,REPLACE(columnname,'$',''))
FROM #TEST
WHERE ISNUMERIC(REPLACE(columnname,'$',''))=1
AND CONVERT(MONEY,REPLACE(columnname,'$','')) % 1 != 0
You can use like
Select * from Yourtablename where Yourcolumnname like '$15.%'
Two things:
First, you want to find the rows having the $ in them.
WHERE LOCATE('$',columname) <> 0
Second, you want to find the rows where the rest of the value in the column is a floating point number.
AND CONVERT(REPLACE(columnname,'$',''),DECIMAL(10,2)) <> 0
That CONVERT() <>0 pattern works because MySQL silently returns zero when you try to convert a nonnumeric value to a number.

TSQL: How do I combine two values (money/int) using UNION ALL without changing the data?

I have the following Query:
create table #Result (Reward varchar(40), Value MONEY);
insert #Result exec GetCurrentCycleQualifierStatusByAccountId #AccountId=76011;
with cteFirstResults as
(select Reward, round(Value,2) as Value from #Result where Reward like '%Balance%'),
cteSecondResults as
(select Reward, convert(INTEGER, Value) as Value from #Result where Reward NOT like '%Balance%')
select * from cteFirstResults
UNION ALL
select * from cteSecondResults;
drop table #Result;
When running a select * individually against each "cte" table, I get the results I want.
But when run all together, I get something like:
Reward Value
------ -----
Daily Balance 4709.00
Value A 1.00
Value B 9.00
I want the Value A/Value B data to show without any decimal values as they do when running a select against the table directly. How do I combine the two queries into one to show this data correctly?
Round(value,0) does nothing.
I can not change the sproc from which I'm gathering the data, but I can make the temp table any way I like.
Thanks,
Jason
The solution:
create table #Result (Reward varchar(40), Value MONEY);
insert #Result exec GetCurrentCycleQualifierStatusByAccountId #AccountId=76011;
With cteFirstResults as
(
Select Reward, Value
From #Result
Where Reward like '%Balance%'
)
, cteSecondResults as
(
Select Reward, cast(Value as int) as Value
From #Result
Where Reward Not like '%Balance%'
)
Select Reward, Cast( Value As varchar(max)) As Value
From cteFirstResults
Union All
Select Reward, Cast( value As varchar(max)) as Value
From cteSecondResults;
drop table #Result;
The problem is the integers are being implicitly cast to decimal because they are being represented in a decimal column.
If you just want the values displayed, cast them both to strings.
CREATE TABLE #test
(
test decimal(9,2)
)
CREATE TABLE #test2
(
test int
)
INSERT INTO #test (test)
SELECT 1.25 UNION ALL
SELECT 172813.99
INSERT INTO #test2 (test)
SELECT 134 UNION ALL
SELECT 41
SELECT CAST(test as varchar(max)) FROM #Test
UNION ALL
SELECT CAST(test as varchar(max)) FROM #Test2
Results:
1.25
172813.99
134
41
In a union SQL Server will assume that the datatype of the second select is the same as the first and where it can convert them, do so. You will have to beat it at its own game and do you own conversion
In the final select (the one with the Union) massage the data in both cases to be a string. Format the output as desired before converting it to a string.
EACH Sql Server column can only have 1 data type.
round(money,4) returns a money(4)*
convert(int) returns an int
Based on data type precedence
#13. money
#16. int
The resultant column is money(4). Therefore ALL values in the column will be formatted using money(4).
Your options are
convert(float) across both - downside: a value of 1.1 is shown as 1.1, not 1.10
convert(varchar) - you have stated you don't want this, and it changes the data type to the receiving program
FWIW
Round(value,0) does nothing.
It does do something. It burns CPU rounding an int value to another int value (of the same value). Incidentally, the resultant type is (still) "int". This has nothing to do with formatting.
REF:
declare #m money
set #m = 12.3233
select SQL_VARIANT_PROPERTY(round(#m,2), 'basetype') -- money
select SQL_VARIANT_PROPERTY(round(#m,2), 'precision') -- 19
select SQL_VARIANT_PROPERTY(round(#m,2), 'scale') -- 4

How do I extract part of a string in t-sql

If I have the following nvarchar variable - BTA200, how can I extract just the BTA from it?
Also, if I have varying lengths such as BTA50, BTA030, how can I extract just the numeric part?
I would recommend a combination of PatIndex and Left. Carefully constructed, you can write a query that always works, no matter what your data looks like.
Ex:
Declare #Temp Table(Data VarChar(20))
Insert Into #Temp Values('BTA200')
Insert Into #Temp Values('BTA50')
Insert Into #Temp Values('BTA030')
Insert Into #Temp Values('BTA')
Insert Into #Temp Values('123')
Insert Into #Temp Values('X999')
Select Data, Left(Data, PatIndex('%[0-9]%', Data + '1') - 1)
From #Temp
PatIndex will look for the first character that falls in the range of 0-9, and return it's character position, which you can use with the LEFT function to extract the correct data. Note that PatIndex is actually using Data + '1'. This protects us from data where there are no numbers found. If there are no numbers, PatIndex would return 0. In this case, the LEFT function would error because we are using Left(Data, PatIndex - 1). When PatIndex returns 0, we would end up with Left(Data, -1) which returns an error.
There are still ways this can fail. For a full explanation, I encourage you to read:
Extracting numbers with SQL Server
That article shows how to get numbers out of a string. In your case, you want to get alpha characters instead. However, the process is similar enough that you can probably learn something useful out of it.
substring(field, 1,3) will work on your examples.
select substring(field, 1,3) from table
Also, if the alphabetic part is of variable length, you can do this to extract the alphabetic part:
select substring(field, 1, PATINDEX('%[1234567890]%', field) -1)
from table
where PATINDEX('%[1234567890]%', field) > 0
LEFT ('BTA200', 3) will work for the examples you have given, as in :
SELECT LEFT(MyField, 3)
FROM MyTable
To extract the numeric part, you can use this code
SELECT RIGHT(MyField, LEN(MyField) - 3)
FROM MyTable
WHERE MyField LIKE 'BTA%'
--Only have this test if your data does not always start with BTA.
declare #data as varchar(50)
set #data='ciao335'
--get text
Select Left(#Data, PatIndex('%[0-9]%', #Data + '1') - 1) ---->>ciao
--get numeric
Select right(#Data, len(#data) - (PatIndex('%[0-9]%', #Data )-1) ) ---->>335

Resources