how to convert persian number to int - sql

There is a nvarchar(100) column named value, when users insert into this column I need to check this code below in a trigger:
if exists
(
select *
from inserted i
where isnumeric(value)=0
)
begin
rollback transaction
raiserror('when productType is numeric, You have to insert numeric character',18,1)
return
end
but in application interface numbers inserted in persian, so always isnumeric(value)=0.
For example I need to if user insert ۴۵ in interface in my trigger value shown as 45.
So far I use CAST,CONVERT and collate Persian_100_CI_AI but I couldn't get any result.
Thanks.

Which version of SQL Server? v2017+ offers a new function TRANSLATE.
Might be, there is a more elegant way, but a pragmatic one is this:
DECLARE #PersianNumber NVARCHAR(100)=N'۴۵';
SELECT CAST(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE
(#PersianNumber,N'۰',N'0'),N'۱',N'1'),N'۲',N'2'),N'۳',N'3'),N'۴',N'4')
,N'۵',N'5'),N'۶',N'6'),N'۷',N'7'),N'۸',N'8'),N'۹',N'9') AS INT);

Take a look at this topic, it's the opposite of what you asked but it might help you if you could reverse it :
https://social.msdn.microsoft.com/Forums/sqlserver/en-US/a44ce5c1-d487-4043-be73-b64fa98ed7a5/converting-english-numbers-to-arabic-numbers-and-vice-versa
If you are using the latest version of sql server, try this link :
https://learn.microsoft.com/en-us/sql/t-sql/functions/translate-transact-sql
the obvious thing is that SQL does not have a solution out-of-the-box and you have to implement some kind of function yourself and use the returned value in the WHERE statement.
I have used Shungo's answer to implement the function you need (also works for English numbers or a mix of both):
CREATE FUNCTION IS_NORMALIZED_NUMBER (#PersianNumber NVARCHAR(MAX))
RETURNS NVARCHAR(MAX)
BEGIN
SET #PersianNumber = CAST(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE
(#PersianNumber,N'۰',N'0'),N'۱',N'1'),N'۲',N'2'),N'۳',N'3'),N'۴',N'4')
,N'۵',N'5'),N'۶',N'6'),N'۷',N'7'),N'۸',N'8'),N'۹',N'9') AS NVARCHAR(MAX));
RETURN ISNUMERIC(#PersianNumber)
END
Here is a more optimized version (which will only work for Persian numbers) :
CREATE FUNCTION IS_NUMBER (#PersianNumber NVARCHAR(MAX))
RETURNS NVARCHAR(MAX)
BEGIN
RETURN IIF(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE
(#PersianNumber,N'۰',N''),N'۱',N''),N'۲',N''),N'۳',N''),N'۴',N'')
,N'۵',N''),N'۶',N''),N'۷',N''),N'۸',N''),N'۹',N'') = N'',1 ,0 );
END

You can use TRANSLATE (Transact-SQL) function
SELECT TRANSLATE('1234', '0123456789', N'٠١٢٣٤٥٦٧٨٩') AS KurdishNumber

Related

Read from Value-Typed Function

I've been looking in ithe internet but couldnt find a solution. So i decide to ask here:
I've a table contains specifications of my products. It has colum names as: productCode, specName, specStr, visibility
I need to put all specs in a single string. Something like (for productcode=54S4): "Category: Electronics, Weight: 50g, Brand: XYZ"
I've to do this using sql server functions. So i created a value-typed function. i got specs of a single product in format of a table. Here is the function:
CREATE FUNCTION getDescription1 (#code varchar(32))
RETURNS table
AS
RETURN (
SELECT (specName+': '+specStr+', ')as description
FROM productSpecs
where specStr<>'' and visibility=1 and productCode=#code
)
As a result, I have a table. But i couldnt read the data in it using while or something.
Is my route true? If yes what to do now? If no enlighten me please...
CREATE FUNCTION getDescription1 (#code varchar(32))
RETURNS nvarchar(1000)
AS
DECLARE #p_str NVARCHAR(1000)
SET #p_str = ''
SELECT #p_str = #p_str + (specName+': '+specStr+', ')
FROM productSpecs
where specStr<>'' and visibility=1 and productCode=#code
RETURN LEFT(#p_str, len(#p_str) -1)

TSQL function which will raiserror if passed null?

I'm looking to add some code to my TSQL arsenal to defend against performing aggregations in SQL when the data in a column is null. Ideally there would be a SUM_NN (for sum no null), in sql server which would raiserror if any of the values were null.
Since you can't raiserror from a UDF, the only way I could think of doing it looked like this, though I don't like this solution:
CREATE FUNCTION dbo.NULL_TEST_F(#arg FLOAT)
RETURNS FLOAT
AS
BEGIN
IF(#arg IS NULL)
SELECT 1/0
RETURN #arg
END
Note: I think this is stupid solution, but I've gotten burned way too many times when I'm missing data. Also, we're using SQL Server 2005, but I'm open to 2008 and 2012 solutions. Additionally, I'd like to know how other databases deal with this type of issue.
This was my final solution:
CREATE FUNCTION [dbo].[NullTest_F]
(
#input FLOAT,
#message VARCHAR(100)
)
RETURNS FLOAT
AS
BEGIN
DECLARE #test INT
IF(#input IS NULL)
SELECT #test = CAST(#message AS INT)
RETURN #input
END
I can then embed this with a useful error message when running aggregate functions. Example:
CREATE TABLE Data (
Date DATETIME,
DataPoint FLOAT
)
INSERT INTO Data (Date, DataPoint) VALUES ('2012-03-01', 4)
INSERT INTO Data (Date, DataPoint) VALUES ('2012-03-02', 6)
SELECT SUM(NullTest_F(DataPoint, 'Missing data at' + CONVERT(VARCHAR(10), Data))
FROM Data
Maybe this one will help:
https://stackoverflow.com/a/4681815/1371070
You could create a function like suggested in the answer linked above and call it from your aggregate in case #arg is null.
It's still the same strategy overall but It's a better error to throw than divide-by-zero, I guess.

SQL: SELECT number text base on a number

Background: I have an SQL database that contain a column (foo) of a text type and not integer. In the column I store integer in a text form.
Question: Is it possible to SELECT the row that contains (in foo column) number greater/lesser than n?
PS: I have a very good reason to store them as text form. Please refrain from commenting on that.
Update: (Forgot to mention) I am storing it in SQLite3.
SELECT foo
FROM Table
WHERE CAST(foo as int)>#n
select *
from tableName
where cast(textColumn as int) > 5
A simple CAST in the WHERE clause will work as long as you are sure that the data in the foo column is going to properly convert to an integer. If not, your SELECT statement will throw an error. I would suggest you add an extra step here and take out the non-numeric characters before casting the field to an int. Here is a link on how to do something similar:
http://blog.sqlauthority.com/2007/05/13/sql-server-udf-function-to-parse-alphanumeric-characters-from-string/
The only real modification you would need to do on this function would be to change the following lines:
PATINDEX('%[^0-9A-Za-z]%', #string)
to
PATINDEX('%[^0-9]%', #string)
The results from that UDF should then be castable to an int without it throwing an error. It will further slow down your query, but it will be safer. You could even put your CAST inside the UDF and make it one call. The final UDF would look like this:
CREATE FUNCTION dbo.UDF_ParseAlphaChars
(
#string VARCHAR(8000)
)
RETURNS int
AS
BEGIN
DECLARE #IncorrectCharLoc SMALLINT
SET #IncorrectCharLoc = PATINDEX('%[^0-9]%', #string)
WHILE #IncorrectCharLoc > 0
BEGIN
SET #string = STUFF(#string, #IncorrectCharLoc, 1, '')
SET #IncorrectCharLoc = PATINDEX('%[^0-9]%', #string)
END
SET #string = #string
RETURN CAST(#string as int)
END
GO
Your final SELECT statement would look something like this:
SELECT *
FROM Table
WHERE UDF_ParseAlphaChars(Foo) > 5
EDIT
Based upon the new information that the database is SQLite, the above probably won't work directly. I don't believe SQLite has native support for UDFs. You might be able to create a type of UDF using your programming language of choice (like this: http://www.christian-etter.de/?p=439)
The other option I see to safely get all of your data (an IsNumeric would exclude certain rows from your results, which might not be what you want) would probably be to create an extra column that has the int representation of the string. It is a little more dangerous in that you need to keep two fields in sync, but it will allow you to quickly sort and filter the table data.
SELECT *
FROM Table
WHERE CAST(foo as int) > 2000

Sql get first 2 groups of a number

I have a number formatted in the following manner: 123.456.789.1.2.3
I want a function that retrieves the first 2 groups of the number and puts them in a bigint: result: 123456
What I've done so far:
CREATE FUNCTION bl.fn_get_2_groups
(
#str nvarchar(255)
)
RETURNS bigint
AS BEGIN
DECLARE #newStr nvarchar(255)
select #newStr = SUBSTRING(#str,1,charindex('.',#str)-1), #str = SUBSTRING(#str,charindex('.',#str)+1,LEN(#str))
select #newStr += SUBSTRING(#str,1,charindex('.',#str)-1)
return convert(bigint,#newStr)
END GO
Does anyone know a different way to do this? Maybe more elegant or shorter?
This should work:
cast(REPLACE(LEFT(#str,CHARINDEX('.', #str,CHARINDEX('.', #str)+1)),'.','') as bigint)
Your method looks OK. If you want something much more elegant or easier, I think you are going to have to write a CLR function. Then, you could avail yourself of all that .NET API has to offer for string parsing. Alternatively, you could try to do this parsing in the application layer. Of course, that may not be feasible.
You can read about regexs and CLR functions here:
http://msdn.microsoft.com/en-us/magazine/cc163473.aspx
Also, I would suggest amending your SQL UDF to ensure that the input string does in fact have two periods.

T-SQL User defined function overloading?

I understand that T-SQL is not object oriented. I need to write a set of functions that mimics method overloading in C#.
Is function overloading supported in T-SQL in any way? If there is a hack to do this, is it recommended?
No, there is no way to do this.
I recommend you revisit the requirement, as "make apples look like oranges" is often difficult to do, and of questionable value.
One thing I have done successfully is to write the function in such a way as to allow it to handle null values, and then call it with nulls in place of the parameters you would like to omit.
Example:
create function ActiveUsers
(
#departmentId int,
#programId int
)
returns int
as
begin
declare #count int
select #count = count(*)
from users
where
departmentId = isnull(#departmentId, departmentId)
and programId = isnull(#programId, programId)
return #count
end
go
Uses:
select ActiveUsers(1,3) -- users in department 1 and program 3
select ActiveUsers(null,3) -- all users in program 3, regardless of department
select ActiveUsers(null,null) -- all users
You could pass in a sql_variant, but it comes with all sorts of hazards around it; you can't really use strong typing like you can with OO languages and overloading.
If you need to find the base type within your function, you can use the SQL_VARIANT_PROPERTY function.
You can pass in an array of values within a single string and parse them out using this techique by Erland Sommarskog.
Create a function with a varchar(max) parameter or several if necessary, then have your parameter values in that string like:
param1;param2;parma3;param4
or
param1:type;param2:type;param3:type
or
calltype|param1;param2;param3
etc, you are only limited by your imagination...
Use the technique from the link to split apart this array and use program logic to use those values as you wish.
One solution would be to utilize the sql_variant data type. This example works as long as you use the same datatype for both values. Returns whatever datatype you send it.
create function dbo.Greater(
#val1 sql_variant
,#val2 sql_variant
) returns sql_variant
as
begin
declare #rV sql_variant
set #rV = case when #val1 >= #val2 then #val1
else #val2 end
return #rV
end
go
A solution I've had some luck with is either creating a number of functions that each takes a different data type - or casting all input to NVARCHAR(MAX).
1. creating a number of functions that each takes a different data type
CREATE FUNCTION [dbo].[FunctionNameDatetime2]
CREATE FUNCTION [dbo].[FunctionNameInt]
CREATE FUNCTION [dbo].[FunctionNameString] --(this is not a typo)
CREATE FUNCTION [dbo].[FunctionNameUniqueidentifier]
...
Problem: duplication of code, and a lot functions
2. Cast all input to NVARCHAR(MAX)
CREATE FUNCTION [dbo].[IntToNvarchar]
(
#Key INT
)
RETURNS NVARCHAR(MAX)
AS
BEGIN
RETURN ISNULL(CAST(#Key AS NVARCHAR), '');
END
CREATE FUNCTION [dbo].[FunctionName]
(
#Key NVARCHAR(MAX)
)
RETURNS CHAR(32)
AS
BEGIN
DECLARE #something CHAR(32)
do stuff ...
RETURN #something;
END
SELECT [dbo].[FunctionName]([dbo].[IntToNvarchar](25))
Problem: less elegant code than overloading
I overload Functions all the time, but I happen to know that these kind of issues are often highly dependent on platform.
On our DB2 system, I routinely overload like the following:
CREATE Function Schema1.F1 (parm date)
returns date
return date + 1month;
CREATE Function Schema1.F1 (parm timestamp)
returns date
return date(timestamp) + 1month;
This is actually quite useful when you have multiple queries which have similar formating requirements.
The only problem I have found about this so far, is you better be sure that you want the function because the standard drop function "schema"."name" fails because it cannot determine which function to drop. If anyone knows how to drop overloaded sql functions, let me know!