Parsing Chars In SQL Using PATINDEX - sql-server

I'm trying to validate a string using raw sql;
tried using:
DECLARE #AlphaNumeric varchar(50)
SET #AlphaNumeric = '1017a'
SELECT SUBSTRING(#AlphaNumeric, 1, (PATINDEX('%[^0-9]%', #AlphaNumeric) - 1)) AS 'Numeric',
SUBSTRING(#AlphaNumeric, PATINDEX('%[^0-9]%', #AlphaNumeric), DATALENGTH(#AlphaNumeric)) AS 'Alpha'
But if the user types 101a7a,this doesnt work properly;what i want to do exactly is;
I want the variable always to be, numeric+alphanumeric,lenght doesnt matter.
For example :
2303A OK
23A434A NOT OK
A344 NOT OK.
4324AAC OK
This would be dead easy if i could do it in Regex but sql gives me headaches :(

Letters followed by numbers are OK; Numbers followed by letters aren't; All characters must be letters or numbers. Hence...
select * from yourtable
where yourfield like '%[0-9][a-z]%'
and not (yourfield like '%[a-z][0-9]%')
and not (yourfield like '%[^0-9a-z]%')

I think this will do what you want. At least, it works on your sample data:
with t as (
select '2303A' as col union all
select '23A434A' union all
select 'A344'
)
select *,
(case when col like '%[0-9]%' and
substring(col, patindex('%[A-Z]%', col), len(col)) not like '%[^A-Z]%'
then 'OK'
else 'NOT OK'
end)
from t;
The two conditions are. First check that the character string has a number somewhere. Then, check that there are only letters after the first letter is found. I'm assuming that all letters are uppercase.
EDIT:
There might be an easier way. You can check that a number is followed by a letter somewhere in the string, but that a letter is never followed by a number. For this, you only need like:
select (case when col not like '%[^A-Z0-9]%' and
col like '%[0-9][A-Z]%' and
col not like '%[A-Z][0-9]%'
then 'OK'
else 'NOT OK'
end)

I have an approach that should work in your situation. Basically identify the position of the last integer and compare it to the position of the first non integer. You can get the position of the last integer like this
len(#AlphaNumeric) - PATINDEX('%[0-9]%', Reverse(#AlphaNumeric))+1
and you can get the position of the first non integer like this
PATINDEX('%[^0-9]%', #AlphaNumeric)
so that would make your where clause (where all integers precede any non integers like this
Where (len(#AlphaNumeric) - PATINDEX('%[0-9]%', Reverse(#AlphaNumeric))+1 ) < PATINDEX('%[^0-9]%', #AlphaNumeric)

Related

SQL Server - How to get last numeric value in the given string

I am trying to get last numeric part in the given string.
For Example, below are the given strings and the result should be last numeric part only
SB124197 --> 124197
287276ACBX92 --> 92
R009321743-16 --> 16
How to achieve this functionality. Please help.
Try this:
select right(#str, patindex('%[^0-9]%',reverse(#str)) - 1)
Explanation:
Using PATINDEX with '%[^0-9]%' as a search pattern you get the starting position of the first occurrence of a character that is not a number.
Using REVERSE you get the position of the first non numeric character starting from the back of the string.
Edit:
To handle the case of strings not containing non numeric characters you can use:
select case
when patindex(#str, '%[^0-9]%') = 0 then #str
else right(#str, patindex('%[^0-9]%',reverse(#str)) - 1)
end
If your data always contains at least one non-numeric character then you can use the first query, otherwise use the second one.
Actual query:
So, if your table is something like this:
mycol
--------------
SB124197
287276ACBX92
R009321743-16
123456
then you can use the following query (works in SQL Server 2012+):
select iif(x.i = 0, mycol, right(mycol, x.i - 1))
from mytable
cross apply (select patindex('%[^0-9]%', reverse(mycol) )) as x(i)
Output:
mynum
------
124197
92
16
123456
Demo here
Here is one way using Patindex
SELECT RIGHT(strg, COALESCE(NULLIF(Patindex('%[^0-9]%', Reverse(strg)), 0) - 1, Len(strg)))
FROM (VALUES ('SB124197'),
('287276ACBX92'),
('R009321743-16')) tc (strg)
After reversing the string, we are finding the position of first non numeric character and extracting the data from that position till the end..
Result :
-----
124197
92
16

Sql Server's regex LIKE - behaviour clarification?

Someone asked here how to get only values which are a number :
So , if the table is :
DECLARE #Table TABLE(
Col nVARCHAR(50)
)
INSERT INTO #Table SELECT 'ABC'
INSERT INTO #Table SELECT '234.62'
INSERT INTO #Table SELECT '10:10:10:10'
INSERT INTO #Table SELECT 'France'
INSERT INTO #Table SELECT '2'
then - the desired results are :
234.62
2
But when I tested this query :
SELECT * FROM #Table WHERE Col LIKE '%[0-9.]%' --expected to see only 234.62
it showed :
234.62
10:10:10:10
2
Question #1
How come 10:10:10:10 , 2 satisfies the condition ?
Question #2
I saw this answer here which does work
SELECT * FROM #Table WHERE Col NOT LIKE '%[^0-9.]%'
But I don't understand why this works. AFAIU - it selects all values which are not like (not(has number) and not( has dot)) which is ===>(de morgan)===> not like ( has number or has dot)
Can someone please shed light ?
nb I already know that isnumeric can be used also , but it's unsafe (+). also valid wildcards are %,_,[],[^]
Any particular use of [set] within a LIKE expression is a check against one character in the target string.
So, LIKE '%[0-9.]%' says - % - match 0-to-many arbitrary characters, then [0-9.] match one character in the set 0-9., and then % match 0-to-many arbitrary characters. Paraphrased, it says "match any string that contains at least one character in the set 0-9.". So, 10:10:10:10 can be matched as 0 arbitrary characters, then 1 matches [0-9.], and then 0:10:10:10 matches the final %.
LIKE '%[^0-9.]%' says - % - match 0-to-many arbitrary characters, then [^0-9.] match one character not in the set 0-9., and then % match 0-to-many arbitrary characters. Paraphrased, it says "match any string that contains at least one character outside of the set 0-9.. So when we apply the NOT to the front of that, we are saying "match any string that doesn't contain at least one character outside of the set 0-9." or "match strings that only contain characters in the set 0-9..
Essentially, the double-negative is a way to make an assertion about all characters in the string.

Natural sort for SQL Server?

I have a column that is typically only numbers (sometimes it's letters, but that's not important).
How can I make it natural sort?
Currently sorts like this: {1,10,11,12,2,3,4,5,6,7,8,9}
I want it to sort like this: {1,2,3,4,5,6,7,8,9,10,11,12}
IsNumeric is "broken", ISNUMERIC(CHAR(13)) returns 1 and CAST will fail.
Use ISNUMERIC(textval + 'e0'). Final code:
ORDER BY
PropertyName,
CASE ISNUMERIC(MixedField + 'e0') WHEN 1 THEN 0 ELSE 1 END, -- letters after numbers
CASE ISNUMERIC(MixedField + 'e0') WHEN 1 THEN CAST(MixedField AS INT) ELSE 0 END,
MixedField
You can mix order parameters...
Cast it. Also, don't forget to use IsNumeric to make sure you only get the numbers back (if they include letters it IS important ;).
SELECT textval FROM tablename
WHERE IsNumeric(textval) = 1
ORDER BY CAST(textval as int)
Also, cast to the datatype that will hold the largest value.
If you need the non-numbers in the result set too then just append a UNION query where IsNumeric = 0 (order by whatever you want) either before or after.
Have you tied using:
'OrderBy ColumnName Asc'
at the end of your query.

valid record in SQL query

I have a table with few columns and one of the column is DockNumber. I have to display the docknumbers if they confirm to a particular format
First five characters are numbers followed by a - and followed by 5 characters. The last but one character should be a alpha.
12345-678V9
How can I check in SQL if the first 5 characters are numbers and there is a hyphen and next 3 are numbers and last but one is an alpha.
Building on #gbn's answer, this checks to make sure the length is 11 (in case the #val is not a char(11) or varchar(11) and also checks to make sure the second to last char is alpha
DECLARE #val VARCHAR(20)
SET #val = '12345-678V9'
SELECT CASE WHEN LEN(#val) = 11 AND #val LIKE '[0-9][0-9][0-9][0-9][0-9]-[0-9][0-9][0-9][A-Z0-9][0-9]'
THEN 'isMatch'
ELSE 'isNotMatch'
END AS [Valid]
you can use this, you will have to figure it out on how to use this...
SELECT Case when
Cast(ISNUMERIC(LEFT(#Str,5)) as int) + case when substring(#str,6,1)= '-' then 1 else 0 end +case when substring(#str,10,1) like '[a-z]' then 1 else 0 end =3
THEN 'Matched'
Else 'NotMatched'
End
Regular Expressions can be your friend.
LIKE '[0-9][0-9][0-9][0-9][0-9]-[0-9][0-9][0-9][A-Z][0-9]'
Now, this allows lower case a-z too. You'd need to coerce collation if you wanted upper case only
Value COLLATE Latin_General_BIN
LIKE '[0-9][0-9][0-9][0-9][0-9]-[0-9][0-9][0-9][A-Z][0-9]' COLLATE Latin_General_BIN
PATINDEX is probably the ideal solution.
Select ...
From Table
Where PatIndex('[0-9][0-9][0-9][0-9][0-9]-[0-9][0-9][0-9][A-Z][0-9]', DockNumber) > 0
Where DockNumber Like '[0-9][0-9][0-9][0-9][0-9][-][0-9][0-9][0-9][a-z][0-9]
should work, but i would suggest using Regular expression in code. Much easier if it is possible.
The regex should be '^\d{5}-\d{3}[A-Z]\d$', because without ^ and $ it would find longer strings that contain that sequence (122 12345-678V9 34).
Use rule
CREATE RULE pattern_rule
AS
#value LIKE '[0-9][0-9][0-9][0-9]-[0-9][0-9][0-9][0-9][A-Z][0-9]'
Then bind rule to column

SQL Server: sort a column numerically if possible, otherwise alpha

I am working with a table that comes from an external source, and cannot be "cleaned". There is a column which an nvarchar(20) and contains an integer about 95% of the time, but occasionally contains an alpha. I want to use something like
select * from sch.tbl order by cast(shouldBeANumber as integer)
but this throws an error on the odd "3A" or "D" or "SUPERCEDED" value.
Is there a way to say "sort it like a number if you can, otherwise just sort by string"? I know there is some sloppiness in that statement, but that is basically what I want.
Lets say for example the values were
7,1,5A,SUPERCEDED,2,5,SECTION
I would be happy if these were sorted in any of the following ways (because I really only need to work with the numeric ones)
1,2,5,7,5A,SECTION,SUPERCEDED
1,2,5,5A,7,SECTION,SUPERCEDED
SECTION,SUPERCEDED,1,2,5,5A,7
5A,SECTION,SUPERCEDED,1,2,5,7
I really only need to work with the
numeric ones
this will give you only the numeric ones, sorted properly:
SELECT
*
FROM YourTable
WHERE ISNUMERIC(YourColumn)=1
ORDER BY YourColumn
select
*
from
sch.tbl
order by
case isnumeric(shouldBeANumber)
when 1 then cast(shouldBeANumber as integer)
else 0
end
Provided that your numbers are not more than 100 characters long:
WITH chars AS
(
SELECT 1 AS c
UNION ALL
SELECT c + 1
FROM chars
WHERE c <= 99
),
rows AS
(
SELECT '1,2,5,7,5A,SECTION,SUPERCEDED' AS mynum
UNION ALL
SELECT '1,2,5,5A,7,SECTION,SUPERCEDED'
UNION ALL
SELECT 'SECTION,SUPERCEDED,1,2,5,5A,7'
UNION ALL
SELECT '5A,SECTION,SUPERCEDED,1,2,5,7'
)
SELECT rows.*
FROM rows
ORDER BY
(
SELECT SUBSTRING(mynum, c, 1) AS [text()]
FROM chars
WHERE SUBSTRING(mynum, c, 1) BETWEEN '0' AND '9'
FOR XML PATH('')
) DESC
SELECT
(CASE ISNUMERIC(shouldBeANumber)
WHEN 1 THEN
RIGHT(CONCAT('00000000',shouldBeANumber), 8)
ELSE
shouoldBeANumber) AS stringSortSafeAlpha
ORDEER BY
stringSortSafeAlpha
This will add leading zeros to all shouldBeANumber values that truly are numbers and leave all remaining values alone. This way, when you sort, you can use an alpha sort but still get the correct values (with an alpha sort, "100" would be less than "50", but if you change "50" to "050", it works fine). Note, for this example, I added 8 leading zeros, but you only need enough leading zeros to cover the largest possible integer in your column.

Resources