Convert number to words in SSRS - reporting-services

Is there a way in ssrs to convert a numeric value to actual text words in SSRS? i know there is a "towords" function in Crystal Reports, though I am not sure if it is standard, i.e. may require some sort of API download. is this functionality available in SSRS? If not, can the conversion be performed in SQL Server? My reports run off of a SQL Server stored procedure.
Thanks

If you are using SQL Server to return your dataset, then you can use the following function
CREATE FUNCTION [dbo].[udf_CurrencyToWords] (#Input numeric(15,2),#Currency tinyint)
RETURNS varchar (8000)
AS
BEGIN
DECLARE #CharInput char(15)
--Pad the input
SET #CharInput = RIGHT('0000000000000' + convert(varchar(15),#Input),15)
DECLARE #Counter tinyint
SET #Counter = 1
DECLARE #InputSub varchar(3)
DECLARE #Group tinyint
DECLARE #Output varchar (8000)
--Get words for each group with some logic for concatenation
WHILE #Counter <= len(#CharInput)-2
BEGIN
SET #InputSub = replace(substring(#CharInput,#Counter,3),'.','')
SET #Group = (len(#CharInput)-#Counter+1)/3
SET #Output =
ISNULL(#Output,'')
+ CASE
WHEN (#Group = 1 AND #InputSub <> 0 AND #Input >= 1.00) OR (#Group = 2 AND #InputSub BETWEEN 1 AND 99 AND #Input > 1000) THEN 'And '
ELSE ''
END
+ CASE
WHEN (#Group = 1 AND #InputSub = 0 AND #Input >= 1.00) OR (#Group = 2 AND #InputSub = 0 AND #Input < 1.00) THEN ''
ELSE [dbo].[udf_CurrencyToWordsSub](#InputSub,#Group,#Currency)
END
SET #Counter = #Counter + 3
CONTINUE
END
--Fix plurals and return a plain Zero if required
SET #Output =
CASE
WHEN right(#CharInput,2) = '01' THEN replace(#Output,'~','')
ELSE replace(#Output,'~','s')
END
SET #Output =
CASE
WHEN #Input < 2 THEN replace(#Output,'#','')
ELSE replace(#Output,'#','s')
END
RETURN ltrim(rtrim(#Output))
END
Then you can call the function like this
select dbo.[udf_CurrencyToWords] (8990.03,3)
result
Eight Thousand, Nine Hundred And Ninety Dollars And Three Cents
Just change the #currency for the currency you want
select dbo.[udf_CurrencyToWords] (8990.03,1)
Eight Thousand, Nine Hundred And Ninety Pounds And Three Pence

Related

sql create function error code 1064

I need to create a function that I found here: http://vyaskn.tripod.com/code/propercase.txt It converts text to "ProperCase," first letter of every word to uppercase.
CREATE FUNCTION PROPERCASE
(
-- The string to be converted to proper case
#input VARCHAR( 8000 )
)
-- This function returns the proper case string of varchar type
RETURNS VARCHAR( 8000 )
AS
BEGIN
IF #input IS NULL
BEGIN
-- Just return NULL if input string is NULL
RETURN NULL
END
-- Character variable declarations
DECLARE #output VARCHAR( 8000 )
-- Integer variable declarations
DECLARE #ctr INT, #len INT, #found_at INT
-- Constant declarations
DECLARE #LOWER_CASE_a INT, #LOWER_CASE_z INT, #Delimiter CHAR(3), #UPPER_CASE_A INT, #UPPER_CASE_Z INT
-- Variable/Constant initializations
SET #ctr = 1
SET #len = LEN(#input)
SET #output = ''
SET #LOWER_CASE_a = 97
SET #LOWER_CASE_z = 122
SET #Delimiter = ' ,-'
SET #UPPER_CASE_A = 65
SET #UPPER_CASE_Z = 90
WHILE #ctr <= #len
BEGIN
-- This loop will take care of reccuring white spaces
WHILE CHARINDEX(SUBSTRING(#input,#ctr,1), #Delimiter) > 0
BEGIN
SET #output = #output + SUBSTRING(#input,#ctr,1)
SET #ctr = #ctr + 1
END
IF ASCII(SUBSTRING(#input,#ctr,1)) BETWEEN #LOWER_CASE_a AND #LOWER_CASE_z
BEGIN
-- Converting the first character to upper case
SET #output = #output + UPPER(SUBSTRING(#input,#ctr,1))
END
ELSE
BEGIN
SET #output = #output + SUBSTRING(#input,#ctr,1)
END
SET #ctr = #ctr + 1
WHILE CHARINDEX(SUBSTRING(#input,#ctr,1), #Delimiter) = 0 AND (#ctr <= #len)
BEGIN
IF ASCII(SUBSTRING(#input,#ctr,1)) BETWEEN #UPPER_CASE_A AND #UPPER_CASE_Z
BEGIN
SET #output = #output + LOWER(SUBSTRING(#input,#ctr,1))
END
ELSE
BEGIN
SET #output = #output + SUBSTRING(#input,#ctr,1)
END
SET #ctr = #ctr + 1
END
END
RETURN #output
END
I need a function to do that, but it's giving me error...
You are using MySQL, but the syntax you have is for SQL Server. Please read the documentation about MySQL's syntax and convert your procedure to use that syntax. The major constructs are the same, but the syntax is a little different. Here are some things to start with:
Local variables never start with #.
IF is condition followed by THEN followed by any number of lines of code, followed by END IF. The BEGIN...END construct is not used for IF statements in MySQL.
The functions are different. You won't use CHARINDEX but instead INSTR.
Here's the relevant MySQL documentation: http://dev.mysql.com/doc/refman/5.5/en/stored-routines-syntax.html and http://dev.mysql.com/doc/refman/5.5/en/sql-syntax-compound-statements.html.

Get data from comma separated string input

I have a stored procedure where input is a comma separated string say '12341,34567,12446,12997' and it is not sure that the input string always carries numerical data. It may be '12341,34as67,12$46,1we97' so I need to validate them and use only the valid data in query.
Say my query is (Where the column OrderCode is int type)
select * from dbo.DataCollector where OrderCode in (12341,34567,12446,12997)
or only the valid data if other are invalid
select * from dbo.DataCollector where OrderCode in (12341)
For such situation what would be a good solution.
One way that works also in SQl-Server 2005 would be to create a split-function, then you can use ISNUMERIC to check if it's a number:
DECLARE #Input VARCHAR(MAX) = '12341,34as67,12$46,1we97'
SELECT i.Item FROM dbo.Split(#Input, ',')i
WHERE IsNumeric(i.Item) = 1
Demo
Your complete query:
select * from dbo.DataCollector
where OrderCode in ( SELECT i.Item FROM dbo.Split(#Input, ',')i
WHERE IsNumeric(i.Item) = 1 )
Here is the split-function which i use:
CREATE FUNCTION [dbo].[Split]
(
#ItemList NVARCHAR(MAX),
#delimiter CHAR(1)
)
RETURNS #ItemTable TABLE (Item VARCHAR(250))
AS
BEGIN
DECLARE #tempItemList NVARCHAR(MAX)
SET #tempItemList = #ItemList
DECLARE #i INT
DECLARE #Item NVARCHAR(4000)
SET #i = CHARINDEX(#delimiter, #tempItemList)
WHILE (LEN(#tempItemList) > 0)
BEGIN
IF #i = 0
SET #Item = #tempItemList
ELSE
SET #Item = LEFT(#tempItemList, #i - 1)
INSERT INTO #ItemTable(Item) VALUES(#Item)
IF #i = 0
SET #tempItemList = ''
ELSE
SET #tempItemList = RIGHT(#tempItemList, LEN(#tempItemList) - #i)
SET #i = CHARINDEX(#delimiter, #tempItemList)
END
RETURN
END
Edit according to the comment of Damien that ISNUMERIC has it's issues. You can use this function to check if it's a real integer:
CREATE FUNCTION dbo.IsInteger(#Value VarChar(18))
RETURNS Bit
AS
BEGIN
RETURN IsNull(
(Select Case When CharIndex('.', #Value) > 0
Then Case When Convert(int, ParseName(#Value, 1)) <> 0
Then 0
Else 1
End
Else 1
End
Where IsNumeric(#Value + 'e0') = 1), 0)
END
Here is another example with damien's "bad" input which contains £ and 0d0:
Demo

MySQL - min_word_length 2 or 3 - need a count

I am trying to decide whether to set min_word_length to be 2 or 3 on my new MySQL instance, so i figure if I count the number of 2 letter words in the column to be indexed it will give an indication of the right answer.
So, question is, is it possible using a SQL query to count the number two letter words within a column?
Not tested, but could you try
SELECT SUM(
CASE WHEN yourColumn REGEXP '[[:<:]][a-zA-Z]{2}[[:>:]]'
THEN 1
ELSE 0 END
) AS matches
FROM ...
Although I think, that it might only count if a 2-character-word is in a row, not how many times in one row.
UPDATE: Tested and it works like I thought, so please just ignore this answer.
The LEN() function returns the length of the value in a text field.
SELECT LEN(column_name) FROM table_name
The COUNT(*) function returns the number of records in a table:
SELECT COUNT(column_name) FROM table_name
So your query should be something like this :
SELECT COUNT(column_name) FROM table_name
WHERE LEN(column_name)=2
UPDATE:
Ok sorry I guess I misunderstood you. In the case you want it I'm afraid there is no build in functions to do what you want it to do. So you have to make a function on your own. Something like this should work:
CREATE FUNCTION [dbo].[WordCount] ( #InputString VARCHAR(4000) )
RETURNS INT
AS
BEGIN
DECLARE #Index INT
DECLARE #Char CHAR(1)
DECLARE #PrevChar CHAR(1)
DECLARE #WordCount INT
DECLARE #CharCount INT
SET #Index = 1
SET #WordCount = 0
WHILE #Index <= LEN(#InputString)
BEGIN
SET #Char = SUBSTRING(#InputString, #Index, 1)
SET #CharCount = #CharCount + 1
SET #PrevChar = CASE WHEN #Index = 1 THEN ' '
ELSE SUBSTRING(#InputString, #Index - 1, 1)
END
IF #PrevChar = ' '
SET #CharCount = 1
END
IF #Char = ' ' AND #CharCount < 3 AND #CharCount > 1
SET #WordCount = #WordCount + 1
SET #Index = #Index + 1
END
RETURN #WordCount
END
Now I have not tested it so you have to test it on your own but it should work. It will return all 2 letter words in the selected string. To get the 3 letters words just change
IF #Char = ' ' AND #CharCount < 4 AND #CharCount > 2
I hope this helps.

T-SQL strip all non-alpha and non-numeric characters

Is there a smarter way to remove all special characters rather than having a series of about 15 nested replace statements?
The following works, but only handles three characters (ampersand, blank and period).
select CustomerID, CustomerName,
Replace(Replace(Replace(CustomerName,'&',''),' ',''),'.','') as CustomerNameStripped
from Customer
One flexible-ish way;
CREATE FUNCTION [dbo].[fnRemovePatternFromString](#BUFFER VARCHAR(MAX), #PATTERN VARCHAR(128)) RETURNS VARCHAR(MAX) AS
BEGIN
DECLARE #POS INT = PATINDEX(#PATTERN, #BUFFER)
WHILE #POS > 0 BEGIN
SET #BUFFER = STUFF(#BUFFER, #POS, 1, '')
SET #POS = PATINDEX(#PATTERN, #BUFFER)
END
RETURN #BUFFER
END
select dbo.fnRemovePatternFromString('cake & beer $3.99!?c', '%[$&.!?]%')
(No column name)
cake beer 399c
Create a function:
CREATE FUNCTION dbo.StripNonAlphaNumerics
(
#s VARCHAR(255)
)
RETURNS VARCHAR(255)
AS
BEGIN
DECLARE #p INT = 1, #n VARCHAR(255) = '';
WHILE #p <= LEN(#s)
BEGIN
IF SUBSTRING(#s, #p, 1) LIKE '[A-Za-z0-9]'
BEGIN
SET #n += SUBSTRING(#s, #p, 1);
END
SET #p += 1;
END
RETURN(#n);
END
GO
Then:
SELECT Result = dbo.StripNonAlphaNumerics
('My Customer''s dog & #1 friend are dope, yo!');
Results:
Result
------
MyCustomersdog1friendaredopeyo
To make it more flexible, you could pass in the pattern you want to allow:
CREATE FUNCTION dbo.StripNonAlphaNumerics
(
#s VARCHAR(255),
#pattern VARCHAR(255)
)
RETURNS VARCHAR(255)
AS
BEGIN
DECLARE #p INT = 1, #n VARCHAR(255) = '';
WHILE #p <= LEN(#s)
BEGIN
IF SUBSTRING(#s, #p, 1) LIKE #pattern
BEGIN
SET #n += SUBSTRING(#s, #p, 1);
END
SET #p += 1;
END
RETURN(#n);
END
GO
Then:
SELECT r = dbo.StripNonAlphaNumerics
('Bob''s dog & #1 friend are dope, yo!', '[A-Za-z0-9]');
Results:
r
------
Bobsdog1friendaredopeyo
I faced this problem several years ago, so I wrote a SQL function to do the trick. Here is the original article (was used to scrape text out of HTML). I have since updated the function, as follows:
IF (object_id('dbo.fn_CleanString') IS NOT NULL)
BEGIN
PRINT 'Dropping: dbo.fn_CleanString'
DROP function dbo.fn_CleanString
END
GO
PRINT 'Creating: dbo.fn_CleanString'
GO
CREATE FUNCTION dbo.fn_CleanString
(
#string varchar(8000)
)
returns varchar(8000)
AS
BEGIN
---------------------------------------------------------------------------------------------------
-- Title: CleanString
-- Date Created: March 26, 2011
-- Author: William McEvoy
--
-- Description: This function removes special ascii characters from a string.
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
declare #char char(1),
#len int,
#count int,
#newstring varchar(8000),
#replacement char(1)
select #count = 1,
#len = 0,
#newstring = '',
#replacement = ' '
---------------------------------------------------------------------------------------------------
-- M A I N P R O C E S S I N G
---------------------------------------------------------------------------------------------------
-- Remove Backspace characters
select #string = replace(#string,char(8),#replacement)
-- Remove Tabs
select #string = replace(#string,char(9),#replacement)
-- Remove line feed
select #string = replace(#string,char(10),#replacement)
-- Remove carriage return
select #string = replace(#string,char(13),#replacement)
-- Condense multiple spaces into a single space
-- This works by changing all double spaces to be OX where O = a space, and X = a special character
-- then all occurrences of XO are changed to O,
-- then all occurrences of X are changed to nothing, leaving just the O which is actually a single space
select #string = replace(replace(replace(ltrim(rtrim(#string)),' ', ' ' + char(7)),char(7)+' ',''),char(7),'')
-- Parse each character, remove non alpha-numeric
select #len = len(#string)
WHILE (#count <= #len)
BEGIN
-- Examine the character
select #char = substring(#string,#count,1)
IF (#char like '[a-z]') or (#char like '[A-Z]') or (#char like '[0-9]')
select #newstring = #newstring + #char
ELSE
select #newstring = #newstring + #replacement
select #count = #count + 1
END
return #newstring
END
GO
IF (object_id('dbo.fn_CleanString') IS NOT NULL)
PRINT 'Function created.'
ELSE
PRINT 'Function NOT created.'
GO
I know this is an old thread, but still, might be handy for others.
Here's a quick and dirty (Which I've done inversely - stripping out non-numerics) - using a recursive CTE.
What makes this one nice for me is that it's an inline function - so gets around the nasty RBAR effect of the usual scalar and table-valued functions.
Adjust your filter as needs be to include or exclude whatever char types.
Create Function fncV1_iStripAlphasFromData (
#iString Varchar(max)
)
Returns
Table With Schemabinding
As
Return(
with RawData as
(
Select #iString as iString
)
,
Anchor as
(
Select Case(IsNumeric (substring(iString, 1, 1))) when 1 then substring(iString, 1, 1) else '' End as oString, 2 as CharPos from RawData
UNION ALL
Select a.oString + Case(IsNumeric (substring(#iString, a.CharPos, 1))) when 1 then substring(#iString, a.CharPos, 1) else '' End, a.CharPos + 1
from RawData r
Inner Join Anchor a on a.CharPos <= len(rtrim(ltrim(#iString)))
)
Select top 1 oString from Anchor order by CharPos Desc
)
Go
select * from dbo.fncV1_iStripAlphasFromData ('00000')
select * from dbo.fncV1_iStripAlphasFromData ('00A00')
select * from dbo.fncV1_iStripAlphasFromData ('12345ABC6789!&*0')
If you can use SQL CLR you can use .NET regular expressions for this.
There is a third party (free) package that includes this and more - SQL Sharp .

Check constraint to validate IP address field

I'm working on a project involving C# and a SQL Server 2008 database.
In one of the tables, I have a field (nvarchar(15)) which will contain an IP address.
I'd like to add a check constraint which will validate that the input value is actually an IP address.
I wanted to use a regex to do that, but it seems that this feature is not supported by default. I saw things about writing a customm dll with UDF inside (MSDN tutorial), but I don't really understand how it works (i.e. where should I place the dll ?)
Is there a "simple" way to add such a constraint ?
Any solution is welcome.
Thanks in advance !
There are several way of doing this - the most performant one would probably be a CLR function in the database.
This is because SQL has fairly poor text manipulation tooling and no native RegEx in SQL Server.
As other have said, this is better handled by an application before insertion to the DB.
It shouldn't be handled in the database, it should be handled first and foremost in the application.
There's no harm in then adding a check to the database, but leaving it up to the DB to filter input is very sketchy.
The easiest way I can think of is to create a function like fnCheckIP and use this function in the constraint.
There's no need to use UDF.
create function fnCheckIP(#ip varchar(15)) returns bit
AS
begin
if (#ip is null)
return null
declare #num1 int
declare #num varchar(15)
declare #pos int
while (#ip is not null)
begin
set #pos = IsNull(NullIf(charindex('.', #ip), 0), Len(#ip) + 1)
set #num = substring(#ip, 1, #pos - 1)
if (isnumeric(#num) = 0) or (not cast(#num as int) between 0 and 255)
return cast(0 as bit)
if (len(#ip) - #pos <= 0)
set #ip = null
else
set #ip = NullIf(substring(#ip, #pos + 1, len(#ip) - #pos), '')
end
return cast (1 as bit)
end
go
select dbo.fnCheckIP('127.0.0.1')
select dbo.fnCheckIP('127.0.0.300')
This solution is similar to Paulo's but using either approach will require getting rid of the comma character because isnumeric allows commas which will throw a cast to int error.
CREATE FUNCTION fn_ValidateIP
(
#ip varchar(255)
)
RETURNS int
AS
BEGIN
DECLARE #Result int = 0
IF
#ip not like '%,%' and
len(#ip) <= 15 and
isnumeric(PARSENAME(#ip,4)) = 1 and
isnumeric(PARSENAME(#ip,3)) = 1 and
isnumeric(PARSENAME(#ip,2)) = 1 and
isnumeric(PARSENAME(#ip,1)) = 1 and
cast(PARSENAME(#ip,4) as int) between 1 and 255 and
cast(PARSENAME(#ip,3) as int) between 0 and 255 and
cast(PARSENAME(#ip,2) as int) between 0 and 255 and
cast(PARSENAME(#ip,1) as int) between 0 and 255
set #Result = 1
ELSE
set #Result = 0
RETURN #Result
END
select dbo.fn_ValidateIP('127.0.0.1')
This may not be entirely practical, but one way would be to store the converted string ###-###-###-### into a binary(4) data type. Let the interface fuss around with hyphens and deal with converting the four numbers to binary and back (and this could probably even be done by a caluclated column.) A bit extreme, yes, but with binary(4) you will always be able to turn it into an IP address.
At last about 10 yrs after Oracle, sqlserver got native compilation (with limitations)
ALTER function fn_ValidateIPv4
(
#ip varchar(255)
)
RETURNS int
--WITH EXECUTE AS OWNER, SCHEMABINDING, NATIVE_COMPILATION
AS
BEGIN
--ATOMIC WITH (TRANSACTION ISOLATION LEVEL = SNAPSHOT, LANGUAGE = N'us_english')
/* only sql2016 native Compilation **/
DECLARE #len_ip as int;
SET #len_ip = len(#ip);
DECLARE #firstBlock varchar(4) = '';
DECLARE #secondBlock varchar(4) = '';
DECLARE #thirdBlock varchar(4) = '';
DECLARE #fourthBlock varchar(4) = '';
DECLARE #countDot as smallint = 0;
DECLARE #l_i as smallint = 0;
DECLARE #l_curChar varchar(1) = 'X';
DECLARE #Result int = 0
IF (#len_ip <= 15)
BEGIN
WHILE (#l_i < #len_ip)
BEGIN
set #l_i += 1;
set #l_curChar = substring(#ip,#l_i,1);
if #l_curChar = '.'
SET #countDot += 1
ELSE
BEGIN
IF #l_curChar IN ( '0','1','2','3','4','5','6','7','8','9' )
BEGIN
IF #countDot = 0
SET #firstBlock = #firstBlock + #l_curChar;
IF #countDot = 1
SET #secondBlock = #secondBlock + #l_curChar;
IF #countDot = 2
SET #thirdBlock = #thirdBlock + #l_curChar;
IF #countDot = 3
SET #fourthBlock = #fourthBlock + #l_curChar;
IF #countDot > 3
set #firstBlock = 'AAA'; -- force error
END
ELSE set #firstBlock = 'AAA'; -- force error
END;
END;
IF ( #countDot = 3 and
cast(#fourthBlock as int) between 1 and 255 and
cast(#thirdBlock as int) between 0 and 255 and
cast(#secondBlock as int) between 0 and 255 and
cast(#firstBlock as int) between 0 and 255
)
set #Result = 1;
END;
/*
select dbo.fn_ValidateIPv4( '127.0.0.258' );
*/
RETURN #Result
END;
I had to remove not de-supported built functions isnumeric etc...