sql server - check to see if cast is possible - sql

I have the following code to cast nvarchar to integer:
cast(#value as int)
However I have no control of the parameter #value, hence the code might fail. Is there anyway to check if a cast is possible before doing a cast?

Well, in SQL Server 2012 you could use the new TRY_CAST(), but with SQL Server 2008, you should be able to use ISNUMERIC(), and then include handling for values that do not pass that test.

I've recently answered a question about this and using ISNUMERIC to CAST to an INT won't work by itself. Reason being, ISNUMERIC returns true for non integer numbers (1.5) for example.
Here was a recent answer on the subject:
https://stackoverflow.com/a/14692165/1073631
Consider adding an additional check using CHARINDEX with ISNUMERIC, or what I prefer, use a Regular Expression to validate the data.
And here is a Fiddle demonstrating the problem with using ISNUMERIC on it's own. And the Fiddle using a regular expression instead that works.
DECLARE #Test nvarchar(10)
SET #Test = '1.5'
--Works
SELECT CASE WHEN #Test NOT LIKE '%[^0-9]%' THEN CAST(#Test as int) ELSE 0 END
-- Produces Error
SELECT CASE WHEN ISNUMERIC(#Test) = 1 THEN CAST(#Test as int) ELSE 0 END
Good luck.

I generally use the following, it seems to cover all the situations.
SELECT CASE WHEN 1 = ISNUMERIC(#value + '.0') THEN CAST(#value as int) ELSE 0 END
It takes advantage of the fact that "ISNUMERIC" will not allow two periods. The "TRY_CAST" in SQL Server 2012+ is a much better solution though.

The proper test is:
select (case when isnumeric(val) = 1 and val not like '%e%' and val not like '%.%'
then cast(val as int)
end)
The function isnumeric() returns 1 for anything that looks like a float, so you have to be careful.
You can also use what I consider to be a peculiarity of SQL Server. You can cast the floating value 1.23 to an int, but you cannot cast the string value. So, the following also works:
select (case when isnumeric(val) = 1
then cast(cast(val as float) as int)
end)

Maybe we can do something like this:
declare #value as nvarchar(10) = 'A';
begin try
select cast(#value as int);
end try
begin catch
-- do something
end catch

Use a procedure with a TRY CATCH block to suppress errors
i.e.
CREATE PROCEDURE p_try_cast
#type nvarchar(MAX),
#value nvarchar(MAX)
AS
BEGIN
BEGIN TRY
DECLARE #sql varchar(MAX)
DECLARE #out_table TABLE(value varchar(MAX))
SET #sql = 'SELECT CONVERT(varchar(max), CAST(''' + #value + ''' AS ' + #type + '))'
INSERT #out_table
EXECUTE (#sql)
IF EXISTS ( SELECT 1 FROM #out_table WHERE value = #value)
RETURN 1
RETURN 0
END TRY
BEGIN CATCH
RETURN 0
END CATCH
END
GO
Now you can call that with the passed string and desired type and the proc returns 1 for success and 0 for failure
DECLARE #ret int
-- This returns 0 - Fail
EXEC #ret = p_try_cast 'integer', '1.5'
-- This returns 1 - Success
EXEC #ret = p_try_cast 'integer', '1.5'
-- This returns 0 - Fail
EXEC #ret = p_try_cast 'char(4)', 'HELLO'
-- This returns 1 - Success
EXEC #ret = p_try_cast 'char(4)', 'HELL'

Related

Issue with RAW SQL passed into a Function as an Argument to check if Data Exists

I cannot touch the VB Code to make this part easier on myself. It's handled by a very small number of Devs out of State. So this is a SQL issue only.
I know I could make the change in VB (so don't throw that as a solution) but, us Analysts can't. It's a multi billion dollar Corp and changes like this would go through a rigorous Approval process and would probably be denied.
But, we have the freedom to make Custom Tables/Fields [in the Application] and Edits on a DB Level.
My Goal is to Create a function that Grabs RAW SQL from a Field on the Front End and pass it through as an Argument to prove if it's valid to "Print a Document". It returns True or False. If True, the document prints.
Example:
The Script: This will take the SQL as hmy from property where scode = '26thst' in the Argument.
The Function: Grabs the SQL and just needs to check if the SQL Exists and pass back True or False
I have made the Function and it's legal but, does not seem to produce results. The Document gets skipped. I'm kind of stumped on an OOP process for SQL to prove True/False on an Execute #SQL
CASE
WHEN ISNULL(RS1.sdocument1, '') <> '' AND RS1.balwaysprint1 = 'NO'
AND NYSQLFUNCTION(RS1.SSQL1) = 'TRUE' THEN RS1.RS1.DOCUMENT1
WHEN ISNULL(RS1.sdocument1, '') <> '' AND RS1.balwaysprint1 = 'YES'
THEN RS1.RS1.DOCUMENT1
END "_FILE_1"
CREATE FUNCTION NYSQLFUNCTION(#SQLTEXT NVARCHAR(MAX))
RETURNS VARCHAR(500)AS BEGIN
DECLARE #FILEPRINT AS VARCHAR(5)
IF EXISTS(SELECT #SQLTEXT)
SET #FILEPRINT = 'TRUE'
ELSE
SET #FILEPRINT = 'FALSE'
RETURN #FILEPRINT END;
NOTE! When you are calling a function you generally need to include the schema (default dbo)... so dbo.NYSQLFUNCTION
Also... that function will always return 'TRUE' as far as I can see.
If you want to at least check that some SQL has been supplied you could do this...
ie:
CREATE FUNCTION NYSQLFUNCTION(#SQLTEXT NVARCHAR(MAX))
RETURNS VARCHAR(500) AS
BEGIN
DECLARE #FILEPRINT AS VARCHAR(500)
IF #SQLTEXT IS NOT NULL AND LEN(#SQLTEXT) > 0
SET #FILEPRINT = 'TRUE'
ELSE
SET #FILEPRINT = 'FALSE'
RETURN #FILEPRINT
END;
If you actually want to execute the SQL supplied then you'll want to change the function to do that.
... but you can't call sp_executeSQL to dynamically execute the SQL from within a function (because its a stored procedure.. see Execute Stored Procedure from a Function for more info)
So you can do one of the hideous hacks in that answer above...
Or... you might be able to use a Stored procedure instead of a function (depends on the code calling it)
Or... if you know the query is always going to be asking for entries from the same table you could just parse the text for the parameters. EG.... I have assumed that the SQLTEXT will ALWAYS be asking for an entry from the table 'property' with a where clause of scode = 'something'
CREATE FUNCTION NYSQLFUNCTION(#SQLTEXT NVARCHAR(MAX))
RETURNS VARCHAR(500) AS
BEGIN
DECLARE #FILEPRINT AS VARCHAR(500) = 'FALSE'
-- eg.... #SQLTEXT should be something like.... hmy from property where scode = '26thst'
DECLARE #scode NVARCHAR(500)
DECLARE #scodeStartIndex INT, #sCodeEndIndex INT
SELECT #scodeStartIndex = CHARINDEX(' scode ', #SQLTEXT)
IF #scodeStartIndex > 0
BEGIN
SELECT #scodeStartIndex = CHARINDEX('''', #SQLTEXT, #scodeStartIndex)
IF #scodeStartIndex > 0
BEGIN
SELECT #sCodeEndIndex = CHARINDEX('''', #SQLTEXT, #scodeStartIndex + 1)
IF #sCodeEndIndex > 0
BEGIN
SELECT #scode = SUBSTRING(#SQLTEXT, #scodeStartIndex + 1, #sCodeEndIndex - (#scodeStartIndex + 1))
IF EXISTS (SELECT NULL FROM [property] WHERE scode = #scode)
BEGIN
SELECT #FILEPRINT = 'TRUE'
END
END
END
END
RETURN #FILEPRINT
END;
Or... one last option available to you (depending upon your ability to modify the database) is to create a CLR function that executes the supplied SQL query and returns true/false. You would be able to call a CLR function within your user defined function. Here's an article that might help: https://www.codeproject.com/Articles/1098543/Use-SQL-CLR-Function-to-Overcome-Limitation-of-SQL

Empty string should be NULL when converting to float

Consider the following TSQL code:
declare #a nvarchar(500) = N''
select try_convert(float, #a)
The output is:
0
I need the output to be NULL.
I can do this:
declare #a nvarchar(500) = N''
select case #a
when '' then null
else try_convert(float, #a)
end
and it works just fine.
However, this is just a mock-up. In my real life scenario, instead of #a, there are over 200 NVARCHAR(500) columns, either floats or zero length strings. I need a quick way of converting zero-length strings to NULL (and everything else to float), possibly without having to build 200 separate CASE statements.
I'm not really thrilled with relying the rather inexplicable differences between try_parse() and try_convert()/try_cast(). Instead, I would go for:
try_convert(float, nullif(#a, ''))
This also has the advantage of being quite explicit in what you are trying to accomplish.
You should use Try_parse instead
declare #a nvarchar(500) = N''
select try_parse( #a as float)
returns
NULL
See working demo

Select statement with containing a certain criteria

I have a column that contain these value from a table.
Colum1: dhd-29229 Table: Test
dhd-29199
dhd-00011
My goal is to write a select sql statement that will return the following value: All numeric value and not take into account dhd-
Use like:
where column1 like '%29229'
If you want exactly XXX-29199 in this format, you've to use :
where column1 like '___-29229'
If you want just the 29229, you can use : where column1 like '%29229' but in this case the query will return all values that ends with "29229". you can think to controle the length.
https://www.w3schools.com/sql/sql_like.asp
in T-SQL (sql server):
select substring(colum1, charindex('-',colum1)+1,5) as sub_string from test
If you want a strict 'numbers only' that does not reply upon format or inconsistencies... try writing a UDF :
Create FUNCTION [test].[numOnly]
(#text nvarchar (max))
RETURNS nvarchar (max)
AS
BEGIN
DECLARE
#char nvarchar(1),
#i_idx int,
#temp int,
#out nvarchar(max)
While len(#text) > 0
Begin
Set #char = left(#text,1)
if ascii(#char) between 49 and 57
set #out = isnull(#out,'') + #char
Set #text = RIGHT(#text,len(#text)-1)
End
Return #out
END
Use it thus ...
select [test].[numOnly]('44--(*)"*()£hllk564kkj3') as 'lala'
Result : 445643

using a variable to send parameters to IN clause [duplicate]

This question already has answers here:
Parameterize an SQL IN clause
(41 answers)
Closed 9 years ago.
I have a stored procedure. In this procedure there's a piece of code that look like
...WHERE someParam IN (1,2)...
I should abstract this part, cause an undefined numer of parameters should be considered (not only 1 OR 2). I get this parameter list from vb.net code as a cvs string (i.e. "1,2" or "78, 109" and so on). Pratically my situation will be something like that:
DECLARE #IdParam varchar(100)
SET #IdParam = '1,2'
...
...WHERE someParam IN (#IdParm)...
but this code will clearly produce me an error:
Conversion failed when converting the varchar value '1,2' to data type
int.
What can I do to reach my goal and keep SQL engine quiet? If an optimal solution exist I could consider to modify prexisten VB code.
Edit 1: I wouldn't use that ugly solution of the linked post!
Use a table valued parameter to send in multiple values.
CREATE PROC foo
#IDList SomeType READONLY
AS
...
WHERE someParam IN (SELECT ID FROM #IDList )
...
GO
You can create a function to turn the string into a list...
ALTER FUNCTION [dbo].[fn_MVParamToInt](#RepParam nvarchar(max), #Delim char(1)= ',')
RETURNS #VALUES TABLE (Param int)AS
BEGIN
DECLARE #chrind INT
DECLARE #Piece nvarchar(max)
SELECT #chrind = 1
WHILE #chrind > 0
BEGIN
SELECT #chrind = CHARINDEX(#Delim,#RepParam)
IF #chrind > 0
SELECT #Piece = LEFT(#RepParam,#chrind - 1)
ELSE
SELECT #Piece = #RepParam
IF ISNUMERIC(#Piece) = 1
INSERT #VALUES(Param) VALUES(CAST(#Piece AS int))
SELECT #RepParam = RIGHT(#RepParam,LEN(#RepParam) - #chrind)
IF LEN(#RepParam) = 0 BREAK
END
RETURN
END
Then your query becomes something like...
...WHERE mycolumn IN (SELECT Param FROM dbo.fn_MVParamToInt(yourstring, '.'))

Create a concatenation function in SQL

Suppose I have a table a with one column b and three rows(1,2,3), I would like to create a function that will return '1,2,3' that would be called like this : SELECT FUNC(f), ... FROM ...
In other words, I have a linked table that have more than one rows linked to each rows of the first table and would like to concatenate the content of one column from the second table. In this case, it's a list of names associated with a specific observation.
I was thinking of using a SQL function for that, but I can't remember how... :(
Thanks
Here is an example for SQL Server:
CREATE FUNCTION ConcatenateMyTableValues
(#ID int)
RETURNS varchar(max)
AS
BEGIN
declare #s as varchar(max);
select #s = isnull(#s + ',', '') + MyColumn from MyTable where ID = #ID;
return #s
end
And then you could use it like this:
select t.ID, t.Name, dbo.ConcatenateMyTableValues(t.ID)
from SomeTable t
you can use COALESCE to convert values in one column to csv
http://www.sqlteam.com/article/using-coalesce-to-build-comma-delimited-string
I am not sure how you will be able to create a function
unless you do not mind creating a dynamic sql which can accept a column name and then build sql accordingly at run time.
edit.
this works for SQL Server only.
didn't realize that you have not mentioned any db.
DECLARE #list AS varchar(MAX)
SELECT #list = ISNULL(#list + ',', '') + b
FROM MyTable
SELECT #list AS Result
Here a way to do this recursive (ms sql)
Depends on your technology.
If it is Oracle, check out the stragg function.
http://www.sqlsnippets.com/en/topic-11591.html
If it is MSSQL use the XML PATH trick
http://sqlblogcasts.com/blogs/tonyrogerson/archive/2009/03/29/creating-an-output-csv-using-for-xml-and-multiple-rows.aspx
I guess I'm going to answer my own question since I actually got a way to do it using CURSOR (that keyword I was missing in my thoughs..)
ALTER FUNCTION [dbo].[GET_NOM_MEDECIN_REVISEURS] (#NoAs810 int)
RETURNS NVARCHAR(1000)
AS
BEGIN
IF #NoAs810 = 0
RETURN ''
ELSE
BEGIN
DECLARE #NomsReviseurs NVARCHAR(1000)
DECLARE #IdReviseurs AS TABLE(IdReviseur int)
DECLARE #TempNomReviseur NVARCHAR(50)
SET #NomsReviseurs = ''
DECLARE CurReviseur CURSOR FOR
SELECT DISTINCT Nom FROM T_Ref_Reviseur R INNER JOIN T_Signature S ON R.IdReviseur = S.idReviseur WHERE NoAs810 = #NoAs810
OPEN CurReviseur
FETCH FROM CurReviseur INTO #TempNomReviseur
WHILE ##FETCH_STATUS = 0
BEGIN
SET #NomsReviseurs = #NomsReviseurs + #TempNomReviseur
FETCH NEXT FROM CurReviseur INTO #TempNomReviseur
IF ##FETCH_STATUS = 0
SET #NomsReviseurs = #NomsReviseurs + ' - '
END
CLOSE CurReviseur
RETURN #NomsReviseurs
END
RETURN ''
END