Validating string format using sql server - sql

Check if the string is following the correct format or not. The correct format is as follows:
2 upper case letters; 2 digits; 1 to 30 characters alpha-numerical (case insensitive)
e.g. GB29RBOS60161331926819,
GB29RBOS60161331926819A,
GB29RBOS60161331926819B1
So far this is what i have got...
declare #accountNumber varchar(1000) = 'GB99AERF12FDG8AERF12FDG8AERF12FDG8'
select
case when #accountNumber not like '[A-Z][A-Z][0-9][0-9][0-9a-zA-Z]{30}$'
then 'ERROR' else null end

First, your structure assumes a case sensitive collation. Second, SQL Server doesn't recognize {} or $, so you have to repeat the pattern. However, you want up to 30 characters, so splitting the pieces apart is probably the best solution:
select (case when len(#accountNumber) not between 5 and 34 or
#accountNumber not like '[A-Z][A-Z][0-9][0-9]%' or
right(#accountNumber, 34) like '%[^A-Za-z0-9]%'
then 'ERROR'
end)

I think this should work... taking some tips from John.
declare #table table (i varchar(36))
insert into #table
values
('GR09xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'), --30 x's
('GR09xxxxxxxxxxxxxxxxxxxxxxxxxxxx'), --28 x's
('GR09xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'), --31 x's
('Gx09xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'), --lower case 2'd letter
('G509xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'), --digit second letter
('GRg9xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx') --charcater first number (3rd index)
select
case
when i + replicate('a',case when 34-len(i) < 0 then 0 else 34-len(i) end) not like '[A-Z][A-Z][0-9][0-9]' + replicate('[a-zA-Z0-9]',30)
then 'ERROR' else null end
from #table

Related

ISNUMERIC and TRY_PARSE in SQL Server [duplicate]

What is the best way to determine whether or not a field's value is an integer in SQL Server (2000/2005/2008)?
IsNumeric returns true for a variety of formats that would not likely convert to an integer. Examples include '15,000' and '15.1'.
You can use a like statement but that only appears to work well for fields that have a pre-determined number of digits...
select * where zipcode like '[0-9][0-9][0-9][0-9][0-9]'
I could write a user defined function that attempts to convert a varchar parameter to an int within a try/catch block but I'm checking with the community to see if someone has come across any succient methods to achieve this goal - preferably one that can be used within the where clause of a SQL statement without creating other objects.
Late entry that handles negative
ISNUMERIC(zipcode + '.0e0') --integer
ISNUMERIC(zipcode + 'e0') --decimal
For more see this
1 approach is
zipcode NOT LIKE '%[^0-9]%'
Double negatives, got to love 'em!
If SQL Server 2005+, I'd enable CLR and create the function to support regexes. For SQL Server 2000, see this article for creating a UDF to do the same thing.
Then I'd use the regex: ^\d{5}$
This expression gives 1 for an integer value and 0 otherwise
floor((floor(abs(zipcode)))/abs(zipcode))
Why not just use the following? I can't see to find any cases where it fails.
1 = integer
0 = not integer
null = non-numeric
DECLARE #TestValue nvarchar(MAX)
SET #TestValue = '1.04343234e5'
SELECT CASE WHEN ISNUMERIC(#TestValue) = 1
THEN CASE WHEN ROUND(#TestValue,0,1) = #TestValue
THEN 1
ELSE 0
END
ELSE null
END AS Analysis
It looks like this question needs an updated answer.
Limiting the answer to the question title:
where ISNUMERIC(zipcode) = 1
and zipcode - FLOOR(zipcode) = 0
Expounding based on the text of the question...
Currently-supported versions of SQL Server all support/include the TRY-CONVERT function.
declare #a varchar(100)
set #a = '-1.2a'
--set #a = '-1.2'
--set #a = '-1'
--set #a = '-1.0'
--set #a = '-0'
--set #a = '0'
--set #a = '1'
select #a as 'Value'
, ISNUMERIC(#a) as ISNUMERIC
, case when ISNUMERIC(#a) = 1 and #a - FLOOR(#a) = 0 then 1 else 0 end as ISINTEGER
, case when try_convert(int, #a) >= 0 and left(#a, 1) <> '-' then 1 else 0 end as ISWHOLENUMBER
, case when try_convert(int, #a) > 0 then 1 else 0 end as ISCOUNTINGNUMBER
You'll notice that TRY_CONVERT(INT, -1.0) returns NULL. So TRY_CONVERT(INT, #a) IS NOT NULL is not quite right for ISINTEGER.
case when ISNUMERIC(#a) = 1 and #a - FLOOR(#a) = 0 then 1 else 0 end as ISINTEGER
...works because if ISNUMERIC(#a) = 1 is false, FLOOR(#a) is not evaluated. Reversing the order...
case when #a - FLOOR(#a) = 0 and ISNUMERIC(#a) = 1 then 1 else 0 end as ISINTEGER
...generates an error when the value (#a) is not numeric.
So, for the case of zipcode, assuming you want to verify that a 5-digit zip code is a number and it must be 5 digits (so it can't be zero or less) and would never contain a decimal point (so you don't need to know if 12345.000 is an integer):
where try_convert(int, zipcode) > 0
and len(zipcode) = 5
I came up with the perfect answer for this on another StackO question.
It also proves you cannot use ".0e0" like one user suggests here.
It does so without CLR or non-scalar functions.
Please check it out: https://stackoverflow.com/a/10645764/555798
After moving to sql 2008, I was struggling with isnumeric('\8') returning true but throwing an error when casting to an integer. Apparently forward slash is valid currency for yen or won - (reference http://www.louiebao.net/blog/200910/isnumeric/)
My solution was
case when ISNUMERIC(#str) > 0 and not rtrim(#str) LIKE '[^0-9]%' and not rtrim(#str) LIKE '%[^0-9]' and not rtrim(#str) LIKE '[^0-9]%' then rtrim(#str) else null end
See whether the below code will help.
In the below values only 9, 2147483647, 1234567 are eligible as
Integer. We can create this as function and can use this.
CREATE TABLE MY_TABLE(MY_FIELD VARCHAR(50))
INSERT INTO MY_TABLE
VALUES('9.123'),('1234567'),('9'),('2147483647'),('2147483647.01'),('2147483648'), ('2147483648ABCD'),('214,7483,648')
SELECT *
FROM MY_TABLE
WHERE CHARINDEX('.',MY_FIELD) = 0 AND CHARINDEX(',',MY_FIELD) = 0
AND ISNUMERIC(MY_FIELD) = 1 AND CONVERT(FLOAT,MY_FIELD) / 2147483647 <= 1
DROP TABLE MY_TABLE
I did it using a Case statement:
Cast(Case When Quantity/[# of Days]= Cast(Quantity/[# of Days] as int) Then abs(Quantity/[# of Days]) Else 0 End as int)
To test whether the input value is an integer or not we can use SQL_VARIANT_PROPERTY function of SQL SERVER.
The following SQL Script will take input and test it whether the data type turns out to be integer or not
declare #convertedTempValue bigint, #inputValue nvarchar(255) = '1' --Change '1' to any input value
set #convertedTempValue = TRY_PARSE(#inputValue as bigint) --we trying to convert to bigint
declare #var3 nvarchar(255) = cast (SQL_VARIANT_PROPERTY(#convertedTempValue,'BaseType') as nvarchar(255)) --we using SQL_VARIANT_PROPERTY to find out datatype
if ( #var3 like '%int%')
begin
print 'value is integer'
end
else
begin
print 'value is non integer'
end
go
Really late to this but would this work?
select * from from table
where (ISNUMERIC(zipcode) = 0 OR zipcode like '%.%')
Filters out items that are integers.
Maybe you should only store integer data in integer datatypes.

SQL string '00000' displaying as '0'

Can someone explain why
CASE
WHEN MARK = 'Y'
THEN LEFT('00000', 5-LEN(DATEDIFF(MINUTE, START, FINISH))) + DATEDIFF(MINUTE, START, FINISH)
ELSE '00000'
END AS [Time]
Is displaying as a single 0 instead of 00000 when Mark <> 'Y', and displaying 35 instead of 00035 (as an example)
I would guess SQL is converting it to an integer.
Code like this select cast('00000' as varchar) returns as you wish (00000, 00035) but select cast('00000' as int) returns your results (0, 35 etc)
My understanding is that you are using MSSQL server. If, yes then the default datatype for any variable or nullable column is INT

SQL Between Begins With

I'm attempting to use an alpha between in SQL; however, I want it to be based on the beginnings of the words only. I am using T-SQL on Microsoft SQL Server 2005.
My current SQL is:
SELECT * FROM table WHERE LEFT(word,1) BETWEEN 'a' AND 't
However, this only works for first letter. I'd like to expand this to work for any beginnings of words. For instance, between 'a' and 'ter'.
Now, I am building this dynamically, so I could do:
SELECT * FROM table WHERE LEFT(word,1) >= 'a' AND LEFT(word,3) <= 'ter'
However, I'd like to know if there is a simpler way in SQL to make a dynamic beginning-of-word between.
EDIT:::
Follow up question, words less than the length of the checked value should be considered less than in the between. For instance, me is less than mem so word < 'mem' should include me.
EDIT:::
Attempting using padding, as suggested. The below does work; however, the added 'a's can cause issue. For instance, if we want words between 'a' and 'mera' and the word being checked is 'mer', this will be included because the left trim of 'mer' becomes 'mera' with added characters. I would like a solution that does not include this issue.
DECLARE #lb varchar(50)
DECLARE #ub varchar(50)
SET #lb='ly'
SET #ub='z'
SELECT name
FROM table
WHERE
LEFT(
CASE
WHEN LEN(name) < LEN(#lb) THEN name+REPLICATE('a',LEN(#lb)-LEN(name))
ELSE name
END,
LEN(#lb)
) >= #lb
AND
LEFT(CASE
WHEN LEN(name) < LEN(#ub) THEN name+REPLICATE('a',LEN(#ub)-LEN(name))
ELSE name
END,
LEN(#ub)
) <= #ub
EDIT:::
Attempted solution, although CASE heavy. Mack's solution is better, though this works as well. LEFT('andy', 200000) will return 'andy', not an error as an OO language would, behavior I did not expect.
DECLARE #lb varchar(50)
DECLARE #ub varchar(50)
SET #lb='a'
SET #ub='lyar'
SELECT *
FROM testtable
WHERE
CASE
WHEN LEN(word) < LEN(#lb) THEN 0
WHEN LEFT(word, LEN(#lb)) >= #lb THEN 1
ELSE 0
END = 1
AND
CASE
WHEN LEN(word) < LEN(#ub) THEN
CASE
WHEN LEFT(#ub,LEN(word)) = word THEN 1
ELSE 0
END
WHEN LEFT(word, LEN(#ub)) <= #ub THEN 1
ELSE 0
END = 1
Thanks in advance!
This should work:
SELECT * FROM table WHERE LEFT(word,3) BETWEEN 'a' AND 'ter'
There's no reason why BETWEEN shouldn't be able to compare your three-letter data string to the one-letter 'a'. Any 'axx' will be "greater than" just 'a' by itself, and so will be included.
You need to use the STUFF function to achieve what you are looking for explicitly.
If you follow the link says it deletes a specified number of characters at the end of the string and replaces them with another string. Combine the with the LEN function and we can get you on the road.
--Test Data
DECLARE #table AS TABLE (word char(10))
INSERT INTO #table VALUES ('me')
INSERT INTO #table VALUES ('mem')
INSERT INTO #table VALUES ('tap')
INSERT INTO #table VALUES ('t')
DECLARE #minword char(5)
DECLARE #maxword char(5)
SET #minword='ai'
SET #maxword='t'
--SET #maxword='tb'--(remove the rem at the start of this line to unlock an extra test for comparison...)
--Query
SELECT word
FROM #table
WHERE STUFF(word, LEN(word)+1, 5, 'aaaaa') BETWEEN STUFF(#minword, LEN(#minword)+1, 5, 'aaaaa')
AND STUFF(#maxword, LEN(#maxword)+1, 5, 'aaaaa')
Alternative solution based on your revised requirements:
DECLARE #testtable AS TABLE (word varchar(20))
INSERT INTO #testtable VALUES ('ly')
INSERT INTO #testtable VALUES ('Ly')
INSERT INTO #testtable VALUES ('Zoo')
INSERT INTO #testtable VALUES ('r')
INSERT INTO #testtable VALUES ('traci')
DECLARE #minword varchar(20)
DECLARE #maxword varchar(20)
SET #minword='ly'
SET #maxword='zol'
SELECT word, LEFT(word,LEN(#minword)), LEFT(word,LEN(#maxword)), #minword, #maxword
FROM #testtable
WHERE LEFT(word,LEN(#minword))>=#minword
AND LEFT(word,LEN(#maxword))<=#maxword
If I understand you right you are trying to make this into a proc. If so, what you have will work in a proc with very little change. Something like the following (untested)...
CREATE PROC myProc(#low varchar(30), #high varchar(30)) AS
SELECT * FROM table WHERE
(LEN(word) >= LEN(#low)
AND
(LEN(word) >= LEN(#high)
AND
(LEFT(word, LEN(#low)) >= #low)
AND
LEFT(word, LEN(#high)) <= #high
There are additional conditions to exclude records when 'word' is shorter than either of your parameters. Otherwise, you will get errors on the LEFT function. This may not be 100% but it should get you close.
I believe I've found a working solution. I'm not sure of the speed sacrifices here, but the DB will remain small, so it's a non-issue in my specific case.
I am using C# to build my SQL string with parameters. #lb is the lower bound word-part. #rb is the upper bound word-part. The where clause is inclusive, but could easily be change to exclusive as needed.
SELECT * FROM table
WHERE
CASE
WHEN LEN(word) < LEN(#lb) THEN 0
WHEN LEFT(word, LEN(#lb)) >= #lb THEN 1
ELSE 0
END = 1
AND
CASE
WHEN LEN(word) < LEN(#rb) THEN 1
WHEN LEFT(word, LEN(#rb)) <= #rb THEN 1
ELSE 0
END = 1

How can I generate ID with Prefix, Numeric Number and suffix?

I want to generate an ID in MSSQL Server 2008. Which will be Prefix + Numeric Number + suffix Like 'PV#000001#SV'. Which will be user defined (depends on configuration ) prefix, numeric length, suffix and starting number. Numeric number will be increased every time.
I tied to write this :
Blockquote
ALTER PROCEDURE [dbo].[spACC_SELECT_VOUCHER_NUMBER]
#COMPANY_ID uniqueidentifier,
#VOUCHER_TYPE INT
AS BEGIN
DECLARE #IS_AUTOMETIC BIT = (SELECT VOUCHER_CONFIG_NUMBERING_METHOD
FROM ACC_VOUCHER_CONFIG WHERE
ACC_VOUCHER_CONFIG.VOUCHER_CONFIG_VALUE=#VOUCHER_TYPE )
IF(#IS_AUTOMETIC=1)
BEGIN
SELECT CASE WHEN SUBSTRING(V.VOUCHER_CODE, 7, 23) IS NULL
THEN CASE WHEN VC.VOUCHER_CONFIG_PREFIX IS NULL THEN '' ELSE VC.VOUCHER_CONFIG_PREFIX END +
RIGHT ('0000000000000'+ CAST( VC.VOUCHER_CONFIG_BEGINING_NUMBER AS VARCHAR), VC.VOUCHER_CONFIG_NUMERIC_WIDTH) +
CASE WHEN VC.VOUCHER_CONFIG_SUFFIX IS NULL THEN '' ELSE VC.VOUCHER_CONFIG_SUFFIX END
ELSE CASE WHEN VC.VOUCHER_CONFIG_PREFIX IS NULL THEN '' ELSE VC.VOUCHER_CONFIG_PREFIX END +
RIGHT ('0000000000000'+ CAST((CAST( SUBSTRING(V.VOUCHER_CODE, 7, 23) AS INT)+1) AS VARCHAR), VC.VOUCHER_CONFIG_NUMERIC_WIDTH) +
CASE WHEN VC.VOUCHER_CONFIG_SUFFIX IS NULL THEN '' ELSE VC.VOUCHER_CONFIG_SUFFIX END
END AS VOUCHER_CODE FROM ACC_VOUCHER_CONFIG VC
LEFT OUTER JOIN ACC_VOUCHER V ON VC.VOUCHER_CONFIG_VALUE = V.VOUCHER_TYPE
WHERE VC.COMPANY_ID=#COMPANY_ID AND VC.VOUCHER_CONFIG_VALUE=#VOUCHER_TYPE
END
END
When I change the numeric length / suffix its not working.
Thanks
Nahid
For the six-digit number you're struggling with, add leading zeroes like this:
SELECT RIGHT('00000'+ CONVERT(VARCHAR,Num),6) AS NUM FROM your_table
Where Num is your sequential number.
This prepends 5 zeroes and then takes the right 6 characters from the resulting string.
A more detailed writeup of custom ID generation is here:
http://www.sqlteam.com/article/custom-auto-generated-sequences-with-sql-server
My suggestion would be to store just a number in the database (i.e. an int) and format the ID client side with tools that are better suited for it (i.e. a programming language that has sprintf or equivalent string formatting).

Best equivalent for IsInteger in SQL Server

What is the best way to determine whether or not a field's value is an integer in SQL Server (2000/2005/2008)?
IsNumeric returns true for a variety of formats that would not likely convert to an integer. Examples include '15,000' and '15.1'.
You can use a like statement but that only appears to work well for fields that have a pre-determined number of digits...
select * where zipcode like '[0-9][0-9][0-9][0-9][0-9]'
I could write a user defined function that attempts to convert a varchar parameter to an int within a try/catch block but I'm checking with the community to see if someone has come across any succient methods to achieve this goal - preferably one that can be used within the where clause of a SQL statement without creating other objects.
Late entry that handles negative
ISNUMERIC(zipcode + '.0e0') --integer
ISNUMERIC(zipcode + 'e0') --decimal
For more see this
1 approach is
zipcode NOT LIKE '%[^0-9]%'
Double negatives, got to love 'em!
If SQL Server 2005+, I'd enable CLR and create the function to support regexes. For SQL Server 2000, see this article for creating a UDF to do the same thing.
Then I'd use the regex: ^\d{5}$
This expression gives 1 for an integer value and 0 otherwise
floor((floor(abs(zipcode)))/abs(zipcode))
Why not just use the following? I can't see to find any cases where it fails.
1 = integer
0 = not integer
null = non-numeric
DECLARE #TestValue nvarchar(MAX)
SET #TestValue = '1.04343234e5'
SELECT CASE WHEN ISNUMERIC(#TestValue) = 1
THEN CASE WHEN ROUND(#TestValue,0,1) = #TestValue
THEN 1
ELSE 0
END
ELSE null
END AS Analysis
It looks like this question needs an updated answer.
Limiting the answer to the question title:
where ISNUMERIC(zipcode) = 1
and zipcode - FLOOR(zipcode) = 0
Expounding based on the text of the question...
Currently-supported versions of SQL Server all support/include the TRY-CONVERT function.
declare #a varchar(100)
set #a = '-1.2a'
--set #a = '-1.2'
--set #a = '-1'
--set #a = '-1.0'
--set #a = '-0'
--set #a = '0'
--set #a = '1'
select #a as 'Value'
, ISNUMERIC(#a) as ISNUMERIC
, case when ISNUMERIC(#a) = 1 and #a - FLOOR(#a) = 0 then 1 else 0 end as ISINTEGER
, case when try_convert(int, #a) >= 0 and left(#a, 1) <> '-' then 1 else 0 end as ISWHOLENUMBER
, case when try_convert(int, #a) > 0 then 1 else 0 end as ISCOUNTINGNUMBER
You'll notice that TRY_CONVERT(INT, -1.0) returns NULL. So TRY_CONVERT(INT, #a) IS NOT NULL is not quite right for ISINTEGER.
case when ISNUMERIC(#a) = 1 and #a - FLOOR(#a) = 0 then 1 else 0 end as ISINTEGER
...works because if ISNUMERIC(#a) = 1 is false, FLOOR(#a) is not evaluated. Reversing the order...
case when #a - FLOOR(#a) = 0 and ISNUMERIC(#a) = 1 then 1 else 0 end as ISINTEGER
...generates an error when the value (#a) is not numeric.
So, for the case of zipcode, assuming you want to verify that a 5-digit zip code is a number and it must be 5 digits (so it can't be zero or less) and would never contain a decimal point (so you don't need to know if 12345.000 is an integer):
where try_convert(int, zipcode) > 0
and len(zipcode) = 5
I came up with the perfect answer for this on another StackO question.
It also proves you cannot use ".0e0" like one user suggests here.
It does so without CLR or non-scalar functions.
Please check it out: https://stackoverflow.com/a/10645764/555798
After moving to sql 2008, I was struggling with isnumeric('\8') returning true but throwing an error when casting to an integer. Apparently forward slash is valid currency for yen or won - (reference http://www.louiebao.net/blog/200910/isnumeric/)
My solution was
case when ISNUMERIC(#str) > 0 and not rtrim(#str) LIKE '[^0-9]%' and not rtrim(#str) LIKE '%[^0-9]' and not rtrim(#str) LIKE '[^0-9]%' then rtrim(#str) else null end
See whether the below code will help.
In the below values only 9, 2147483647, 1234567 are eligible as
Integer. We can create this as function and can use this.
CREATE TABLE MY_TABLE(MY_FIELD VARCHAR(50))
INSERT INTO MY_TABLE
VALUES('9.123'),('1234567'),('9'),('2147483647'),('2147483647.01'),('2147483648'), ('2147483648ABCD'),('214,7483,648')
SELECT *
FROM MY_TABLE
WHERE CHARINDEX('.',MY_FIELD) = 0 AND CHARINDEX(',',MY_FIELD) = 0
AND ISNUMERIC(MY_FIELD) = 1 AND CONVERT(FLOAT,MY_FIELD) / 2147483647 <= 1
DROP TABLE MY_TABLE
I did it using a Case statement:
Cast(Case When Quantity/[# of Days]= Cast(Quantity/[# of Days] as int) Then abs(Quantity/[# of Days]) Else 0 End as int)
To test whether the input value is an integer or not we can use SQL_VARIANT_PROPERTY function of SQL SERVER.
The following SQL Script will take input and test it whether the data type turns out to be integer or not
declare #convertedTempValue bigint, #inputValue nvarchar(255) = '1' --Change '1' to any input value
set #convertedTempValue = TRY_PARSE(#inputValue as bigint) --we trying to convert to bigint
declare #var3 nvarchar(255) = cast (SQL_VARIANT_PROPERTY(#convertedTempValue,'BaseType') as nvarchar(255)) --we using SQL_VARIANT_PROPERTY to find out datatype
if ( #var3 like '%int%')
begin
print 'value is integer'
end
else
begin
print 'value is non integer'
end
go
Really late to this but would this work?
select * from from table
where (ISNUMERIC(zipcode) = 0 OR zipcode like '%.%')
Filters out items that are integers.
Maybe you should only store integer data in integer datatypes.