Separate numeric inside string variable using patindex - sql

I have here a short problem.
i have a value like this
1/11
11/1
11/11
111/1
111/11
i'm trying to separate those value and put them into numeric variable.
let say for example i have numericvar1 and numericvar2
so in the 1st string numericvar1 will contain 1 and numericvar2 will contain 11
and so on.
i've tried it like this
SET #numericvar1= LEFT(#StrNumHolder, PATINDEX('%[0-9][^0-9]%', #StrNumHolder ))
SET #numericvar2= REPLACE(RIGHT(#StrNumHolder, PATINDEX('%[0-9][^0-9]%', #StrNumHolder )),'/','')
in this code if the first number before / is in 2 digit i got the correct output. But if the first number is in 1 digit and the next number is in 2 digit like 1/11 i got the wrong output. something like this var1 = 1 and var2 = 1
did something wrong in my code? or it is not possible? please help me.

You can use CHARINDEX to get the position of '/' then use SUBSTRING to separate the numbers. Here how you can query it:
DECLARE #nIndex INT
SELECT #nIndex = CHARINDEX('/',#StrNumHolder)
SET #numericvar1 = SELECT SUBSTRING(#StrNumHolder,1,#nIndex-1)
SET #numericvar2 = SELECT SUBSTRING(#StrNumHolder,#nIndex+1,LEN(#StrNumHolder))

Can you try something like this?
DECLARE #STRNUMHOLDER VARCHAR(20)
SET #STRNUMHOLDER='111/1'
DECLARE #pos INT
SET #pos = CHARINDEX('/', #STRNUMHOLDER)
SELECT #STRNUMHOLDER AS ORIG, LEFT(#STRNUMHOLDER, #pos-1) AS NUM1, SUBSTRING(#STRNUMHOLDER, #pos+1,99) AS NUM2

Related

Joining on numeric part of string

It's been a while...I'd like to get your advice on the most efficient way to join on only the number part of a field that may be prefixed and/or suffixed with up to 2 letters. Here's a simplified snippet of what I'm trying to do:
SELECT a, b, c
FROM table 1 t1
LEFT JOIN table 2 t2 ON t1.PolicyCode = t2.sPolicyID,
Where t2.sPolicyID could begin and/or end with up to 2 letters. Some examples: TG73100, S7286674, 2344506R, etc. We only want to join to just its numeric part in between the letters, i.e. 73100, 7286674 or 2344506 from the examples.
Could someone please advise on a simple way of doing this?
Here is one way:
LEFT JOIN table 2 t2 ON t1.PolicyCode =
LEFT(SUBSTRING(t2.sPolicyID, PATINDEX('%[0-9]%', t2.sPolicyID), 50),
PATINDEX('%[^0-9]%',
SUBSTRING(t2.sPolicyID, PATINDEX('%[0-9]%', t2.sPolicyID), 50)
+ 'a') -1)
To break this down, there are 4 main parts.
1: Find the position of the first number with PATINDEX:
DECLARE #spolicyID VARCHAR(20) = 'xx123123xx'
SELECT PATINDEX('%[0-9]%', #spolicyID)
--Returns 3
2: Use SUBSTRING() to cut off everything before the first letter:
DECLARE #spolicyID VARCHAR(20) = 'xx123123xx'
SELECT SUBSTRING(#spolicyID, PATINDEX('%[0-9]%', #spolicyID), 50)
--Returns 123123xx
If we hardcoded the 3 that we know is returned from the first part, it would look like this:
DECLARE #spolicyID VARCHAR(20) = 'xx123123xx'
SELECT SUBSTRING(#spolicyID, 3), 50)
--50 is the number of characters to extract, set to something
--higher than the max string length to be safe
Of course, we don't want to hardcode it since it can change, but that makes seeing the different functions a bit easier.
3: Find the position of the next letter using PATINDEX again:
DECLARE #spolicyID VARCHAR(20) = 'xx123123xx'
SELECT PATINDEX('%[^0-9]%', SUBSTRING(#spolicyID, PATINDEX('%[0-9]%', #spolicyID), 50) + 'a')
--Returns 7 since it is looking at 123123xx
--The first x is in the 7th position
Note that we added an a onto the string. This is because if we had a string with no letters at the end, it would throw an error as the length 0 would be returned to SUBSTRING. You could add any letter or letters to the end and it would work, we are just making sure there is at least one. Try removing the + 'a' and using a string like xx123123 to see the error.
If we hardcoded the 123123xx from step 2 it would look like this (again just for easy example):
DECLARE #spolicyID VARCHAR(20) = 'xx123123xx'
SELECT PATINDEX('%[^0-9]%', '123123xx' + 'a')
4: Use LEFT() to return everything before the trailing letters, leaving us with only the numbers in between:
DECLARE #spolicyID VARCHAR(20) = 'xx123123xx'
LEFT(SUBSTRING(#spolicyID, PATINDEX('%[0-9]%', #spolicyID), 50),PATINDEX('%[^0-9]%', SUBSTRING(#spolicyID, PATINDEX('%[0-9]%', #spolicyID), 50) + 'a') -1)
--Need to add `-1` because step 3 PATINDEX returns 7
--as the position of first trailing letter, and
--we want the 6 characters before that
And again hardcoded from step 2 and 3 for easy viewing:
DECLARE #spolicyID VARCHAR(20) = 'xx123123xx'
LEFT('123123xx', 7-1)

Pattern Matching with SQL Like for a range of characters

Is there a way to use Pattern Matching with SQL LIKE, to match a variable number of characters with an upper limit?
For example, I have one column which can have "correct values" of 2-10 numbers, anything more than 10 and less than 2 is incorrect.
If you really want to use like you can do:
where col like '__' or col_like '___' or . . .
or:
where col like '__%' and -- at least two characters
col not like '_________%' -- not 9 characters
The more typical method would be:
where len(col) between 2 and 10
If you want to ensure they are numbers:
where len(col) between 2 and 10 and
col not like '%[^0-9]%'
You could make a function to do this the long way of inspecting each character like below:
DECLARE #TempField varchar(max)= '1v3Abc567'
DECLARE #FieldLength int = LEN(#TempField)
DECLARE #CountNumeric int = 0
WHILE #FieldLength > 0
BEGIN
SELECT #CountNumeric = #CountNumeric + ISNUMERIC( SUBSTRING (#TempField,#FieldLength,1))
SET #FieldLength = #FieldLength - 1
END
SELECT #CountNumeric NumericCount
You can use Regex.
SELECT * FROM mytable WHERE mycol LIKE '%[0-9]{2,10}%';
should do it (that's off the top of my head, so double-check!

Extract number between two substrings in sql

I had a previous question and it got me started but now I'm needing help completing this. Previous question = How to search a string and return only numeric value?
Basically I have a table with one of the columns containing a very long XML string. There's a number I want to extract near the end. A sample of the number would be this...
<SendDocument DocumentID="1234567">true</SendDocument>
So I want to use substrings to find the first part = true so that Im only left with the number.
What Ive tried so far is this:
SELECT SUBSTRING(xml_column, CHARINDEX('>true</SendDocument>', xml_column) - CHARINDEX('<SendDocument',xml_column) +10087,9)
The above gives me the results but its far from being correct. My concern is that, what if the number grows from 7 digits to 8 digits, or 9 or 10?
In the previous question I was helped with this:
SELECT SUBSTRING(cip_msg, CHARINDEX('<SendDocument',cip_msg)+26,7)
and thats how I got started but I wanted to alter so that I could subtract the last portion and just be left with the numbers.
So again, first part of the string that contains the digits, find the two substrings around the digits and remove them and retrieve just the digits no matter the length.
Thank you all
You should be able to setup your SUBSTRING() so that both the starting and ending positions are variable. That way the length of the number itself doesn't matter.
From the sound of it, the starting position you want is right After the "true"
The starting position would be:
CHARINDEX('<SendDocument DocumentID=', xml_column) + 25
((adding 25 because I think CHARINDEX gives you the position at the beginning of the string you are searching for))
Length would be:
CHARINDEX('>true</SendDocument>',xml_column) - CHARINDEX('<SendDocument DocumentID=', xml_column)+25
((Position of the ending text minus the position of the start text))
So, how about something along the lines of:
SELECT SUBSTRING(xml_column, CHARINDEX('<SendDocument DocumentID=', xml_column)+25,(CHARINDEX('>true</SendDocument>',xml_column) - CHARINDEX('<SendDocument DocumentID=', xml_column)+25))
Have you tried working directly with the xml type? Like below:
DECLARE #TempXmlTable TABLE
(XmlElement xml )
INSERT INTO #TempXmlTable
select Convert(xml,'<SendDocument DocumentID="1234567">true</SendDocument>')
SELECT
element.value('./#DocumentID', 'varchar(50)') as DocumentID
FROM
#TempXmlTable CROSS APPLY
XmlElement.nodes('//.') AS DocumentID(element)
WHERE element.value('./#DocumentID', 'varchar(50)') is not null
If you just want to work with this as a string you can do the following:
DECLARE #SearchString varchar(max) = '<SendDocument DocumentID="1234567">true</SendDocument>'
DECLARE #Start int = (select CHARINDEX('DocumentID="',#SearchString)) + 12 -- 12 Character search pattern
DECLARE #End int = (select CHARINDEX('">', #SearchString)) - #Start --Find End Characters and subtract start position
SELECT SUBSTRING(#SearchString,#Start,#End)
Below is the extended version of parsing an XML document string. In the example below, I create a copy of a PLSQL function called INSTR, the MS SQL database does not have this by default. The function will allow me to search strings at a designated starting position. In addition, I'm parsing a sample XML string into a variable temp table into lines and only looking at lines that match my search criteria. This is because there may be many elements with the words DocumentID and I'll want to find all of them. See below:
IF EXISTS (select * from sys.objects where name = 'INSTR' and type = 'FN')
DROP FUNCTION [dbo].[INSTR]
GO
CREATE FUNCTION [dbo].[INSTR] (#String VARCHAR(8000), #SearchStr VARCHAR(255), #Start INT, #Occurrence INT)
RETURNS INT
AS
BEGIN
DECLARE #Found INT = #Occurrence,
#Position INT = #Start;
WHILE 1=1
BEGIN
-- Find the next occurrence
SET #Position = CHARINDEX(#SearchStr, #String, #Position);
-- Nothing found
IF #Position IS NULL OR #Position = 0
RETURN #Position;
-- The required occurrence found
IF #Found = 1
BREAK;
-- Prepare to find another one occurrence
SET #Found = #Found - 1;
SET #Position = #Position + 1;
END
RETURN #Position;
END
GO
--Assuming well formated xml
DECLARE #XmlStringDocument varchar(max) = '<SomeTag Attrib1="5">
<SendDocument DocumentID="1234567">true</SendDocument>
<SendDocument DocumentID="1234568">true</SendDocument>
</SomeTag>'
--Split Lines on this element tag
DECLARE #SplitOn nvarchar(25) = '</SendDocument>'
--Let's hold all lines in Temp variable table
DECLARE #XmlStringLines TABLE
(
Value nvarchar(100)
)
While (Charindex(#SplitOn,#XmlStringDocument)>0)
Begin
Insert Into #XmlStringLines (value)
Select
Value = ltrim(rtrim(Substring(#XmlStringDocument,1,Charindex(#SplitOn,#XmlStringDocument)-1)))
Set #XmlStringDocument = Substring(#XmlStringDocument,Charindex(#SplitOn,#XmlStringDocument)+len(#SplitOn),len(#XmlStringDocument))
End
Insert Into #XmlStringLines (Value)
Select Value = ltrim(rtrim(#XmlStringDocument))
--Now we have a table with multple lines find all Document IDs
SELECT
StartPosition = CHARINDEX('DocumentID="',Value) + 12,
--Now lets use the INSTR function to find the first instance of '">' after our search string
EndPosition = dbo.INSTR(Value,'">',( CHARINDEX('DocumentID="',Value)) + 12,1),
--Now that we know the start and end lets use substring
Value = SUBSTRING(value,(
-- Start Position
CHARINDEX('DocumentID="',Value)) + 12,
--End Position Minus Start Position
dbo.INSTR(Value,'">',( CHARINDEX('DocumentID="',Value)) + 12,1) - (CHARINDEX('DocumentID="',Value) + 12))
FROM
#XmlStringLines
WHERE Value like '%DocumentID%' --Only care about lines with a document id

find if there is a 6 digit number within a string

How would you advise to find out in Sql Server 2010/2012 if a query contains a substring equal to a 6 digits number?
e.g. "agh123456 dfsdfdf" matches the requirements
"x123 ddd456" doesn't match the requirements because the 6 digits are not consecutive
"lm123" doesn't match the requirements because only 3 digits are found (out of the required 6)
The problem I encountered so far: is that SUBSTRING as a function requires parameters (position where the number presumably starts and this is random)
while PATINDEX returns the location of a pattern in a string, but we don't know the exact pattern (it can be any 6 digit number)
Any pointers or advice, much appreciated.
Thank you
You can use the LIKE operator:
SELECT *
FROM MyTable
WHERE Mycolumn LIKE '%[0-9][0-9][0-9][0-9][0-9][0-9]%'
Even this should work.. considering you don't have a string like this
abc123 abc123456
Try this
DECLARE #str varchar(max) = 'abcxyz123456'
SELECT ISNUMERIC(SUBSTRING(#str,(SELECT PATINDEX('%[0-9]%',#str)),6))
If you want to select all rows in the table and mask the first 6-digit substring in each row:
DECLARE #mask varchar(max) = '######'
DECLARE #pattern varchar(max) = '%'+REPLACE(#mask,'#','[0-9]')+'%'
SELECT
ISNULL(STUFF(col1,PATINDEX(#pattern,col1),LEN(#mask),#mask),col1)
FROM Table1

Substring in tsql using length

Ok, so I am tring to get the first of the last 2 digits of a number. Take for instance 12345601, I want to know if the second to last numeric is a 0. If 0 then I need it to select only the last digit of the int, if different than 0 select the last 2 digits. this is what I have :
declare #myint int
set #myint= 12345601
select case when substring(cast (#myint as varchar(50) ) , len(#myint)-1, len(#myint)-1 ) = 0 then right(#myint, 1)
else right(#myint, 2) end
Unfortunately, it isn't working and this is where:
substring(cast (#myint as varchar(50)), len(#myint)-1, len(#myint)-1 )
that substring is coming out at 01, but I need it to come out as 0. Any ideas?
Try this:
SELECT CASE WHEN LEFT(RIGHT(#myint,2),1) = 0 THEN RIGHT(#myint,1) ELSE RIGHT(#myint,2) END
Try this instead:
SELECT CAST(RIGHT(#myint,2) as int)
If you cast it as an int it should strip the leading zero anyway...
You can see it in action with the code below:
declare #myint int
set #myint= 12345601
SELECT CAST(RIGHT(#myint,2) as int)
set #myint= 12345611
SELECT CAST(RIGHT(#myint,2) as int)