Only assign a variable if it is a numeric value - sql

Is it possible to do something like this?
declare #ShouldBeNumber as varchar(10)
set #ShouldBeNumber = 1
declare #Number as varchar(10)
select #Number = case when ISNUMERIC(#ShouldBeNumber) then #Number = #ShouldBeNumber else #Number = '' end
i.e. only assign the variable if it is numeric. I have spent the last hour Googling this and have not found anything.
For example, I have looked here: How to check if a variable has a value in a SQL Server 2008 stored procedure

I think you just want:
select #Number = (case when ISNUMERIC(#ShouldBeNumber) = 1
then #ShouldBeNumber else #Number
end)
I assume isnumeric() does what you want, although it might have some unwanted behavior depending on your needs (accepting negative numbers, decimals, and exponential notation for instance). The else clause is a no-op; it just assigns the existing value back to #Number.

You can also use the try_cast() function as shown below.
DECLARE #ShouldBeNumber AS VARCHAR(10)
SET #ShouldBeNumber = 'A'
DECLARE #number AS VARCHAR(10)
Select #number = isnull(cast(try_cast(#ShouldBeNumber as float) as varchar(10)),'')
SELECT #number
Live db<>fiddle demo.

Related

How to validate string in SQL with characters

Check if the string is following the correct format or not. The correct format is as follows:
0000/00000. So far this is what i have got:
declare #ID nvarchar = '0000/00000'
SELECT (case when len(#id) not between 1 and 12 OR #id not like( '[0-9][0-9][0-9][0-9]' + '/' + '[0-9][0-9][0-9][0-9][0-9]')
OR LEFT(#id,13) LIKE '%[0-9]&' then 'OK' else 'ERROR' end
Why not just use not like?
where #id not like '[0-9][0-9][0-9][0-9]/[0-9][0-9][0-9][0-9][0-9]'
I assume that "0" means that any digit is allowed.
First, this will be a problem:
declare #ID nvarchar = '0000/00000'
This:
declare #ID nvarchar = '0000/00000';
SELECT #ID LEN(#ID), DATALENGTH(#ID);
Returns:
ID LEN
---- ----
0 1
With that in mind, note that this: PATINDEX({your pattern},#Id) will return a natural Boolean result.
DECLARE #ID1 NVARCHAR(1000) = '5555/12312',
#ID2 NVARCHAR(1000) = '1234/12345678',
#pattern VARCHAR(100) = '[0-9][0-9][0-9][0-9]/[0-9][0-9][0-9][0-9][0-9]';
SELECT IsCool = PATINDEX(#pattern,f.Id)
FROM (VALUES(#ID1),(#ID2)) AS f(Id);
Returns:
IsCool
-----------
1
0
If, by 0000/00000 you mean: Four digits + "/" + five digits then these expressions all work (note I made the pattern a variable for cleaner, easier to read code, it's not required):
DECLARE #ID NVARCHAR(1000) = '5555/12312',
#pattern VARCHAR(100) = '[0-9][0-9][0-9][0-9]/[0-9][0-9][0-9][0-9][0-9]';
SELECT CASE PATINDEX(#Pattern,#ID) WHEN 1 THEN 'Ok' ELSE 'Error' END;
SELECT IIF(PATINDEX(#Pattern,#ID)>0,'Ok','Error');
SELECT CHOOSE(PATINDEX(#Pattern,#ID)+1,'Error','Ok');
Your code suggests that #ID can be up to 12 characters long. Let's say, for example, the acceptable formats were:
0000/00000
0000/000000
0000/0000000
Then you could do this:
DECLARE #ID NVARCHAR(1000) = '5555/12312';
SELECT CASE WHEN LEN(#ID)<13 AND SIGN(PATINDEX('%[0-9][0-9]',#ID)) *
PATINDEX('[0-9][0-9][0-9][0-9]/[0-9][0-9][0-9][0-9][0-9]%',#ID) = 1
THEN 'Ok' ELSE 'Error' END;
Two final thoughts:
LEN({string}) can't be negative; LEN(#ID) < 13 will do the trick
If the max length is really 12 then make the parameter or variable NVARCHAR(13). This way, for example, someone passes in an 8000 character string, SQL doesn't have to scan the whole thing to determine that it's not valid.

how to use declared variable to select data from another table in case when condition?

I made select query in which i want to select data based on condition.For this i declared one variable and set value of that variable in else part.I want to use that variable for further select in same else part how can i achieve this?Please Help
declare #stateid int
select CASE WHEN MstCustomerAddressInfo.StateId is null
THEN 24
ELSE set #stateid = MstCustomerAddressInfo.StateId
select mststate.statecode from mststate where MstState.StateId = #stateid
END AS StateCode
No, you can't have SET inside a CASE expression. Even you can't have multiple statements.
Same query you can write as following.
declare #stateid int
select CASE
WHEN MstCustomerAddressInfo.StateId is null THEN 24
ELSE
-- set #stateid = MstCustomerAddressInfo.StateId
(select mststate.statecode
from mststate
where MstState.StateId = MstCustomerAddressInfo.StateId)
END AS StateCode
from [Your_Table]

Why the output is changed when using case statement?

Can anyone explain the Result?
DECLARE #dec AS VARCHAR(5)
SET #dec = 'Yes'
DECLARE #val DECIMAL(15, 2);
SET #val = - 34152542256.86;
SELECT #val as c1
,CAST(#val AS BIGINT) as c2
SELECT #val as c1
,CASE #dec
WHEN 'Yes'
THEN CAST(#val AS BIGINT)
ELSE #val
END as c2
Results:
of first select statement:
c1 c2
-34152542256.86 -34152542256
of second select statement:
c1 c2
-34152542256.86 -34152542256.00
Source
You have an implicit conversion in your CASE statement. Basically, c2 must have a data-type, but SQL Server doesn't know whether it's DECIMAL(15,2) or BIGINT, as both types are returned from the various branches through the CASE statement. The rules of data-type precedence kick in and c2 ends up being converted to a DECIMAL(15,2).
As a try and I think this could work in this case, try sql_variant data-type like this:
DECLARE #val sql_variant;
In the first select statement the result for c2 can only be BIGINT.
In the second statement the result can be BIGINT or DECIMAL(15,2), belongs to which branch of case statement is entered. Since you can't have mixed types in one column sql server automatically converts c2 to DECIMAL(15,2)
BIGINT is an integer value (it does not allow scale), try using DECIMAL or NUMERIC.
And don't give up reading documentation.

SQL take just the numeric values from a varchar

Say i have a few fields like the following:
abd738927
jaksm234234
hfk342
ndma0834
jon99322
Type: varchar.
How do I take just the numeric values from this to display:
738927
234234
342
0834
99322
Have tried substring however the data varies in length, and cast didnt work either due to being unable to convert, any ideas?
Here's the example with PATINDEX:
select SUBSTRING(fieldName, PATINDEX('%[0-9]%', fieldName), LEN(fieldName))
This assumes (1) the field WILL have a numeric, (2) the numerics are all grouped together, and (3) the numerics don't have any subsequent characters after them.
Extract only numbers (without using while loop) and check each and every character to see if it is a number and extract it
Declare #s varchar(100),#result varchar(100)
set #s='as4khd0939sdf78'
set #result=''
select
#result=#result+
case when number like '[0-9]' then number else '' end from
(
select substring(#s,number,1) as number from
(
select number from master..spt_values
where type='p' and number between 1 and len(#s)
) as t
) as t
select #result as only_numbers
DECLARE #NonNumeric varchar(1000) = 'RGI000Testing1000'
DECLARE #Index int
SET #Index = 0
while 1=1
begin
set #Index = patindex('%[^0-9]%',#NonNumeric)
if #Index <> 0
begin
SET #NonNumeric = replace(#NonNumeric,substring(#NonNumeric,#Index, 1), '')
end
else
break;
end
select #NonNumeric -- 0001000
Well if you don't want to create a function, you can just something like this:
cast(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(
replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(
replace(replace(replace(replace(replace(replace(replace(replace(replace(YOUR_COLUMN
,'A',''),'B',''),'C',''),'D',''),'E',''),'F',''),'G',''),'H',''),'I',''),'J','')
,'K',''),'L',''),'M',''),'N',''),'O',''),'P',''),'Q',''),'R',''),'S',''),'T','')
,'U',''),'V',''),'W',''),'X',''),'Y',''),'Z',''),'$',''),',',''),' ','') as float)
I think you're wanting VBA's Val() function. Easy enough to accomplish with IsNumeric()
create function Val
(
#text nvarchar(40)
)
returns float
as begin
-- emulate vba's val() function
declare #result float
declare #tmp varchar(40)
set #tmp = #text
while isnumeric(#tmp) = 0 and len(#tmp)>0 begin
set #tmp=left(#tmp,len(#tmp)-1)
end
set #result = cast(#tmp as float)
return #result
end
select substring(
'jaksm234234',
patindex('%[0-9]%','jaksm234234'),
LEN('jaksm234234')-patindex('%[0-9]%','jaksm234234')+2
)
input table
if you have data like above in the image, then use the below query
select field_3 from table where PATINDEX('%[ ~`!##$%^&*_()=+\|{};",<>/?a-z]%', field_3)=0
Results will be look like this
Result table
Extract only numbers from a string. Returns a string with all the numbers inside. Example: this1is2one345long6789number will return 123456789
CREATE FUNCTION [dbo].[GetOnlyNumbers] (#Temp VARCHAR(1000))
RETURNS VARCHAR (1000) AS BEGIN
DECLARE #KeepValues AS VARCHAR(50)
SET #KeepValues = '%[^0-9]%'
WHILE PATINDEX(#KeepValues, #Temp) > 0
SET #Temp = STUFF(#Temp, PATINDEX(#KeepValues, #Temp), 1, '')
RETURN #Temp
END
A right with patindex for the reverse string works also for those
SELECT [Column],
CAST(RIGHT([Column], PATINDEX('%[0-9][^0-9]%', REVERSE([Column])+' ')) AS INT) as [Num]
FROM (VALUES
('abd738927'),
('jaksm234234'),
('hfk342'),
('ndma0834'),
('jon99322'),
) val([Column])
Column
Num
abd738927
738927
jaksm234234
234234
hfk342
342
ndma0834
834
jon99322
99322

Is it faster to check if length = 0 than to compare it to an empty string?

I've heard that in some programming languages it is faster to check if the length of a string is 0, than to check if the content is "". Is this also true for T-SQL?
Sample:
SELECT user_id FROM users WHERE LEN(user_email) = 0
vs.
SELECT user_id FROM users WHERE user_email = ''
Edit
You've updated your question since I first looked at it. In that example I would say that you should definitely always use
SELECT user_id FROM users WHERE user_email = ''
Not
SELECT user_id FROM users WHERE LEN(user_email) = 0
The first one will allow an index to be used. As a performance optimisation this will trump some string micro optimisation every time! To see this
SELECT * into #temp FROM [master].[dbo].[spt_values]
CREATE CLUSTERED INDEX ix ON #temp([name],[number])
SELECT [number] FROM #temp WHERE [name] = ''
SELECT [number] FROM #temp WHERE LEN([name]) = 0
Execution Plans
Original Answer
In the code below (SQL Server 2008 - I "borrowed" the timing framework from #8kb's answer here) I got a slight edge for testing the length rather than the contents below when #stringToTest contained a string. They were equal timings when NULL. I probably didn't test enough to draw any firm conclusions though.
In a typical execution plan I would imagine the difference would be negligible and if you're doing that much string comparison in TSQL that it will be likely to make any significant difference you should probably be using a different language for it.
DECLARE #date DATETIME2
DECLARE #testContents INT
DECLARE #testLength INT
SET #testContents = 0
SET #testLength = 0
DECLARE
#count INT,
#value INT,
#stringToTest varchar(100)
set #stringToTest = 'jasdsdjkfhjskdhdfkjshdfkjsdehdjfk'
SET #count = 1
WHILE #count < 10000000
BEGIN
SET #date = GETDATE()
SELECT #value = CASE WHEN #stringToTest = '' then 1 else 0 end
SET #testContents = #testContents + DATEDIFF(MICROSECOND, #date, GETDATE())
SET #date = GETDATE()
SELECT #value = CASE WHEN len(#stringToTest) = 0 then 1 else 0 end
SET #testLength = #testLength + DATEDIFF(MICROSECOND, #date, GETDATE())
SET #count = #count + 1
END
SELECT
#testContents / 1000000. AS Seconds_TestingContents,
#testLength / 1000000. AS Seconds_TestingLength
I would be careful about using LEN in a WHERE clause as it could lead to table or index scans.
Also note that if the field is NULLable that LEN(NULL) = NULL, so you would need to define the behaviour, e.g.:
-- Cost .33
select * from [table]
where itemid = ''
-- Cost .53
select * from [table]
where len(itemid) = 0
-- `NULL`able source field (and assuming we treat NULL and '' as the same)
select * from [table]
where len(itemid) = 0 or itemid is NULL
I just tested it in a very limited scenario and execution plan ever so slightly favours comparing it to an empty string. (49% to 51%). This is working with stuff in memory though so it would probably be different if comparing against data from a table.
DECLARE #testString nvarchar(max)
SET #testString = ''
SELECT
1
WHERE
#testString = ''
SELECT
1
WHERE
LEN(#testString) = 0
Edit: This is with SQL Server 2005.
I suspect the answer depends largely on the context. For example, I have been working with expressions in the SELECT list and in user-defined functions. Knowing what I do about the Microsoft .NET Base Class Library and the legacy that Transact-SQL owes to Visual Basic, I suspect that in such circumstances, LEN ( string ) gets the nod.