Extract number from a string value - sql

I have a varchar that always come into this format:
'PB' + multiple Leading 0 + Number + Non-Number Character(s).
For example: PB000013452S, PB000013452S3s2fss.
How do I parse the varchar value to get the "Number" (13452) in this case?

Use PATINDEX to find the position of the first number (that isn't 0) and then PATINDEX again to find the position of the first non-numerical character afterwards. Then use SUBSTRING to extract the number:
SELECT SUBSTRING(V.YourString,PI.I,PATINDEX('%[^0-9]%',STUFF(V.YourString,1,PI.I-1,''))-1)
FROM (VALUES('PB000013452S'),('PB000013452S3s2fss'))V(YourString)
CROSS APPLY (VALUES(PATINDEX('%[1-9]%',V.YourString)))PI(I)

I write an algorithme for your problem you can try it and i tested befor it works perfectly but i stored numbers in a table and if you want to concatenate them you can use cursor
declare #x varchar(30) = 'PB000013452S3s2fss' /*your string here*/
declare #_len int = len(#x) /*length of your string */
declare #array table (num varchar(30)) /*table for collecte number*/
declare #c int =1 /*counter*/
declare #_char varchar(1) /* to store one char from your string */
declare #result varchar(30)=''
while #_len>0
begin
set #_char = SUBSTRING(#x,#c,1)
if(#_char in ('1','2','3','4','5','6','7','8','9'))
begin
while #_len>0
begin
set #_char = SUBSTRING(#x,#c,1)
if(#_char in ('0','1','2','3','4','5','6','7','8','9'))
begin
insert into #array values (#_char)
set #c = #c+1
set #_len = #_len-1
end
else
set #_len = 0
end
end
set #c = #c+1
set #_len = #_len-1
end
select * from #array

Related

How to order by postcode?

I have a small report that needs to be ordered by postcode. How do I do this?
Using ORDER BY Postcode returns
SK1
SK11
SK13
SK2
How can I return
SK1
SK2
SK11
SK13
EDIT
I should really have added more to the question, I am working with postcodes for the whole of the UK, not just ones starting with SK. So some of these postcodes will start with only 1 letter, some with 2. Also, the second part of the postcode is in the column.
Assuming MSSQL, and that your Postcode field follows a consistent pattern of Char(2) + Number, then you could add a computed query column:
postcode_num = convert(int,substring(postcode,3,len(postcode)))
And then use it instead of Postcode for sorting:
order by postcode_num
Results as desired:
Create 2 columns:
1. a VARCHAR for the first part;
2. a TINYINT for the last (numeric) part.
ORDER BY postcode_prefix, postcode_suffix
Source: https://www.sitepoint.com/community/t/order-by-postcode/50042/9
The problem you are facing is that the column you are trying to ORDER BY is of type text and not numeric, therefore SQL will perform the ordering you're seeing. Instead, if you want SQL to order it as if it was a number then you would need to substring the "SK" part of the column, cast the number characters to numeric type and then order by that.
This is what #LONG replied to you in the first comment.
The way I would approach it is to create a couple of generic functions that will strip the alpha or numeric portions from the string before you sort.
In my example the functions are in the fn schema so change this as you require.
ORDER BY fn.StripToAlpha(PostCode), fn.StripToNumeric(PostCode)
There are plenty of examples of these types of functions around, probably more efficient than the ones I wrote but below is the code to produce the ones I use.
CREATE FUNCTION [fn].[StripToAlpha]
(
#inputString nvarchar(4000)
)
RETURNS varchar(4000)
AS
BEGIN
DECLARE #Counter as int
DECLARE #strReturnVal varchar(4000)
DECLARE #Len as int
DECLARE #ASCII as int
SET #Counter=0
SET #Len=LEN(#inputString)
SET #strReturnVal = ''
WHILE #Counter<=#Len
BEGIN
SET #Counter = #Counter +1
SET #ascii= ASCII(SUBSTRING(#inputString,#counter,1))
IF(#ascii BETWEEN 65 AND 90) OR (#ascii BETWEEN 97 AND 122)
BEGIN
SET #strReturnVal = #strReturnVal + (SUBSTRING(#inputString,#counter,1))
END
END
RETURN #strReturnVal
END
and
CREATE FUNCTION [fn].[StripToNumeric]
(
#inputString nvarchar(4000)
)
RETURNS Float
AS
BEGIN
DECLARE #Counter as int
DECLARE #strReturnVal varchar(4000)
DECLARE #ReturnVal Float
DECLARE #Len as int
DECLARE #ASCII as int
SET #Counter=0
SET #Len=LEN(#inputString)
SET #strReturnVal = ''
IF #inputString IS NULL
BEGIN
Return NULL
END
-- swap out comma for decimal
SET #inputString = REPLACE(#inputString, ',', '.')
IF #Len = 0 OR LEN(LTRIM(RTRIM(#inputString))) = 0
BEGIN
SET #ReturnVal=0
END
ELSE
BEGIN
WHILE #Counter<=#Len
BEGIN
SET #Counter = #Counter +1
SET #ascii= ASCII(SUBSTRING(#inputString,#counter,1))
IF(#ascii BETWEEN 48 AND 57) OR (#ascii IN (46,37))
BEGIN
SET #strReturnVal = #strReturnVal + (SUBSTRING(#inputString,#counter,1))
END
END
if RIGHT(#strReturnVal,1)='%'
BEGIN
SET #strReturnVal = LEFT(#strReturnVal,len(#strReturnVal)-1)
SET #strReturnVal = CAST((CAST(#strReturnVal AS FLOAT)/100) AS nvarchar(4000))
END
SET #ReturnVal = ISNULL(#strReturnVal,0)
END
RETURN #ReturnVal
END
Notes
This will not affect your current use but the StripToNumeric checks is a percentage sign is present and converts to a decimal so it you pass it 25% it will return 0.25.
This will not work if you use full postcodes such as SK1 1AB as it would sort by SKAB and then 11
It will work on postcodes with shorter prefixes such M34 (That's Denton if I remember correctly ! :) )
You didn't specify database you use; this is an Oracle example. Hopefully, you'll be able to "convert" it to something else.
The idea is: using regular expressions (which seem to be quite handy in such cases), split postcode to two parts: letters and numbers. As REGEXP_SUBSTR returns a string, I applied the TO_NUMBER function to a "numeric" part of the postcode in order to properly sort it.
SQL> with test (postcode) as
2 (select 'sk1' from dual union
3 select 'sk11' from dual union
4 select 'sk13' from dual union
5 select 'sk2' from dual
6 )
7 select postcode
8 from test
9 order by regexp_substr(postcode, '^[[:alpha:]]+'), --> letters
10 to_number(regexp_substr(postcode, '[[:digit:]]+$')); --> numbers
POST
----
sk1
sk2
sk11
sk13
SQL>

Substring from first half of a delimated string field

I have a Field value in SQL Server's table which contains the path of file, that path is delimited with - character ,What I want is to select the sub-string from beginning (zero index) to last occurrence of that delimited character.
DECLARE #path NVARCHAR(500)
SELECT #path = 'F:\Fruit Seeds-Category Oil\Quality- Fine Seeds',
I need to extract sub-string "F:\Fruit Seeds-Category Oil\Quality" only
My Query
SELECT LEFT(#path, LEN(#path) - CHARINDEX('-',REVERSE(#path)))
What is the best way to do this?
This finds the last index of "-" in the string:
DECLARE #path NVARCHAR(500) = 'F:\Fruit Seeds-Category Oil\Quality- Fine Seeds';
DECLARE #i INT = 1;
DECLARE #ix INT = 0;
WHILE #i > 0
BEGIN
SET #ix = #i;
SET #i = CHARINDEX('-', #Path, #i + 1);
END
SELECT LEFT(#path, #ix - 1)
You can clearly put it inside a function for reuse.

Inserting/Removing hex padding with SQL Server 2008 R2?

I am working with a database that encodes strings to hex and then pads the entire string with 0's.
Like this:
Origional String: PartitionTest
JUST Hex-encoded Strring: 0x506172746974696f6e54657374
The output I see: 0x50006100720074006900740069006F006E005400650073007400
Is there anyway for me to remove the spaces and decode the final string back to text? AND, how would I then encode that text back to the padded string? I am already converting the string to hex with
SELECT
MASTER.dbo.fn_varbintohexstr(CAST('PartitionTest' AS VARBINARY))
but I don't have a clue how to pad that result out.
Thank you!
EDIT:
As input is varbinary, need to first convert it into varchar to use STUFF.
Declare #input varbinary(128) = 0x50006100720074006900740069006F006E005400650073007400
Declare #Temp as VARCHAR(350)
Set #Temp = CONVERT(VARCHAR(350), #input ,2)
declare #length int
set #length = len(#Temp)
select #length
declare #i int = 3
WHILE #i < #length/2+2
BEGIN
Set #Temp = Stuff(#Temp, #i, 2, '')
set #i = #i +2
END
select '0x' + #Temp

"Simple" Decryption Not Working 100% of the times

I have two functions. An encryption function and a decryption function (see below). The decryption function doesn't work 100% of the time with specific words and I cannot figure out the reason why. Can someone help me figure this out? I am testing this two functions using the following select statements after the functions are in place.
select [dbo].[ufn_EncryptString]('Test1') --This string works
select [dbo].[ufn_DecryptString]('Ôæõ÷µ')
select [dbo].[ufn_EncryptString]('diaz-mayo') --This string doesn't work
select [dbo].[ufn_DecryptString]('äêãý±òçĀ÷')
You would think it has something to do with "-" but there are instances when is just a plain name with no spaces or special character and the string cannot be decrypted.
See functions below:
CREATE FUNCTION [dbo].[ufn_EncryptString] ( #pClearString VARCHAR(100) )
RETURNS NVARCHAR(100) AS
BEGIN
DECLARE #vEncryptedString NVARCHAR(100)
DECLARE #vIdx INT
DECLARE
#vBaseIncrement INT
SET #vIdx = 1
SET #vBaseIncrement = 128
SET #vEncryptedString = ''
WHILE #vIdx <= LEN(#pClearString)
BEGIN
SET #vEncryptedString = #vEncryptedString +
NCHAR(ASCII(SUBSTRING(#pClearString, #vIdx, 1)) +
#vBaseIncrement + #vIdx - 1)
SET #vIdx = #vIdx + 1
END
RETURN #vEncryptedString
END
GO
CREATE FUNCTION [dbo].[ufn_DecryptString] ( #pEncryptedString NVARCHAR(100) )
RETURNS VARCHAR(100) AS
BEGIN
DECLARE #vClearString VARCHAR(100)
DECLARE #vIdx INT
DECLARE #vBaseIncrement INT
SET #vIdx = 1
SET #vBaseIncrement = 128
SET #vClearString = ''
WHILE #vIdx <= LEN(#pEncryptedString)
BEGIN
SET #vClearString = #vClearString +
CHAR(UNICODE(SUBSTRING(#pEncryptedString, #vIdx, 1)) -
#vBaseIncrement - #vIdx + 1)
SET #vIdx = #vIdx + 1
END
RETURN #vClearString
END
GO
I think your function works fine, your example is the issue.
select [dbo].[ufn_DecryptString]('äêãý±òçĀ÷')
Doesn't work... however the following works just fine:
select [dbo].[ufn_DecryptString](N'äêãý±òçĀ÷')
Notice the leading N on the string literal? Since your input parameter is actually a UNICODE string, you need to prefix the literal with N in order to prevent it from being cast to a ASCII string...
From MSDN:
Unicode constants are specified with a leading N: N'A Unicode string'.
Otherwise, when it is converted to ASCII, you're actually passing in äêãý±òçA÷... only the Ā character isn't represented in the CHAR literal, which is why your issue was intermittent.

Is there a simple way to do hexadecimal arithmetic using sql server/TSQL?

I have a column of hexadecimal values in a table. I want to add a hex value to all values in that table. If it were a simple int I would run something like this:
UPDATE myTable
SET num = num + 4000
Is there any way to do this simply using hexadecimal arithmetic? Or do I have to convert the column value to decimal, convert the value I want to add to decimal, add them, and convert the value back to hex? (And if so, what's the simplest way to do that?)
(NOTE: We are currently using sql server 2000.)
use something like :
print convert(varbinary(4),0 + 0x002E + 0x001D)
it should give you a result like :
0x0000004B
the zero in the equation fools it to believe its all numbers so it calculates the value.
Assuming that num is actually a string representation of the hexadecimal number, I think you can convert it to an integer by using a couple of User Defined Functions:
-- Based on Feodor's solution on
-- http://blog.sqlauthority.com/2010/02/01/sql-server-question-how-to-convert-hex-to-decimal/
CREATE FUNCTION fn_HexToInt(#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
-- Example conversion back to hex string - not very tested
CREATE FUNCTION fn_IntToHex(#num int)
RETURNS VARCHAR(16) AS BEGIN
DECLARE #output varchar(16), #rem int
SELECT #output = '', #rem=0
WHILE (#num > 0)
BEGIN
SELECT #rem = #num % 16
SELECT #num = #num / 16
SELECT #output = char(#rem + case when #rem between 0 and 9 then 48 else 55 end) + #output
END
RETURN #output
END
select dbo.fn_HexToInt ('7FFF') -- = 32767
select dbo.fn_IntToHex(32767) -- = 7FFF
So you can try
UPDATE myTable
SET num = dbo.fn_IntToHex(dbo.fn_HexToInt(num) + 4000)
You can use the prefix 0x
eg
Select 0x3F + 2
returns 65
So
UPDATE myTable
SET num = num + 0x4000
(This works in SQL 2008 - I'm not sure if it's new since SQL 2000 - let me know!)
If you have two 0x values, they get concatenated by the + operator, so use convert to convert one of them to an int