Change Case to Upper & Lower in SQL Query SELECT - sql

I have a SQL Query to pull a customer name from database, create a customer folder with that name. In the query I am killing slashes and periods, and i am also changing any result containing MCDONALD to SIMPLY "MCDONALDS". So MCDONALDS #123 comes out as simply MCDONALDS. Here is my query.
SELECT
CASE WHEN CHARINDEX('MCDONALD', cust_name) = 0
THEN REPLACE(REPLACE([cust_name],'.',''),'--','-')
ELSE 'MCDONALDS'
END cust_name
FROM job,dbo.cust cust_name
WHERE job.cust_id_bill_to = cust_name.cust_code AND
job.job_id = '44321' AND
job.sub_job_id = '2'
So, results now are:
MCDONALDS,
BRISTOL-MYERS,
TRUMP-CASINO
Desired results is upper and lower case in every word.. like this:
Mcdonalds,
Bristol-Myers,
Trump-Casino
I know its easy but for me its how to have it work WITH the rest of my query, what I am trying just not working so far.

Check Nimit Dudani Answer Here
DELIMITER $$
DROP FUNCTION IF EXISTS `test`.`initcap`$$
CREATE FUNCTION `initcap`(x char(30)) RETURNS char(30) CHARSET utf8
BEGIN
SET #str='';
SET #l_str='';
WHILE x REGEXP ' ' DO
SELECT SUBSTRING_INDEX(x, ' ', 1) INTO #l_str;
SELECT SUBSTRING(x, LOCATE(' ', x)+1) INTO x;
SELECT CONCAT(#str, ' ', CONCAT(UPPER(SUBSTRING(#l_str,1,1)),LOWER(SUBSTRING(#l_str,2)))) INTO #str;
END WHILE;
RETURN LTRIM(CONCAT(#str, ' ', CONCAT(UPPER(SUBSTRING(x,1,1)),LOWER(SUBSTRING(x,2)))));
END$$
DELIMITER ;
Usage:
select initcap('This is test string');

For Sql Server you can use this
INITCAP User defined Function code
IF OBJECT_ID('dbo.InitCap') IS NOT NULL
DROP FUNCTION dbo.InitCap;
GO
CREATE FUNCTION dbo.InitCap (#inStr VARCHAR(8000))
RETURNS VARCHAR(8000)
AS
BEGIN
DECLARE #outStr VARCHAR(8000) = LOWER(#inStr),
#char CHAR(1),
#alphanum BIT = 0,
#len INT = LEN(#inStr),
#pos INT = 1;
-- Iterate through all characters in the input string
WHILE #pos <= #len BEGIN
-- Get the next character
SET #char = SUBSTRING(#inStr, #pos, 1);
-- If the position is first, or the previous characater is not alphanumeric
-- convert the current character to upper case
IF #pos = 1 OR #alphanum = 0
SET #outStr = STUFF(#outStr, #pos, 1, UPPER(#char));
SET #pos = #pos + 1;
-- Define if the current character is non-alphanumeric
IF ASCII(#char) <= 47 OR (ASCII(#char) BETWEEN 58 AND 64) OR
(ASCII(#char) BETWEEN 91 AND 96) OR (ASCII(#char) BETWEEN 123 AND 126)
SET #alphanum = 0;
ELSE
SET #alphanum = 1;
END
RETURN #outStr;
END
GO
Testing :
SELECT dbo.InitCap('new york');
Result: New York
For your query
SELECT
dbo.InitCap(CASE WHEN CHARINDEX('MCDONALD', cust_name) = 0
THEN REPLACE(REPLACE([cust_name],'.',''),'--','-')
THEN 'MCDONALDS'
END) cust_name
FROM job,dbo.cust cust_name
WHERE job.cust_id_bill_to = cust_name.cust_code AND
job.job_id = '44321' AND
job.sub_job_id = '2'

Related

ADD thousands separators to an Aggregate Sum SQL [duplicate]

I'm running some administrative queries and compiling results from sp_spaceused in SQL Server 2008 to look at data/index space ratios of some tables in my database. Of course I am getting all sorts of large numbers in the results and my eyes are starting to gloss over. It would be really convenient if I could format all those numbers with commas (987654321 becomes 987,654,321). Funny that in all the many years I've used SQL Server, this issue has never come up since most of the time I would be doing formatting at the presentation layer, but in this case the T-SQL result in SSMS is the presentation.
I've considered just creating a simple CLR UDF to solve this, but it seems like this should be do-able in just plain old T-SQL. So, I'll pose the question here - how do you do numeric formatting in vanilla T-SQL?
In SQL Server 2012 and higher, this will format a number with commas:
select format([Number], 'N0')
You can also change 0 to the number of decimal places you want.
While I agree with everyone, including the OP, who says that formatting should be done in the presentation layer, this formatting can be accomplished in T-SQL by casting to money and then converting to varchar. This does include trailing decimals, though, that could be looped off with SUBSTRING.
SELECT CONVERT(varchar, CAST(987654321 AS money), 1)
I'd recommend Replace in lieu of Substring to avoid string length issues:
REPLACE(CONVERT(varchar(20), (CAST(SUM(table.value) AS money)), 1), '.00', '')
For SQL Server 2012+ implementations, you will have the ability to use the FORMAT to apply string formatting to non-string data types.
In the original question, the user had requested the ability to use commas as thousands separators. In a closed as duplicate question, the user had asked how they could apply currency formatting. The following query shows how to perform both tasks. It also demonstrates the application of culture to make this a more generic solution (addressing Tsiridis Dimitris's function to apply Greek special formatting)
-- FORMAT
-- http://msdn.microsoft.com/en-us/library/hh213505(v=sql.110).aspx
-- FORMAT does not do conversion, that's the domain of cast/convert/parse etc
-- Only accepts numeric and date/time data types for formatting.
--
-- Formatting Types
-- http://msdn.microsoft.com/en-us/library/26etazsy.aspx
-- Standard numeric format strings
-- http://msdn.microsoft.com/en-us/library/dwhawy9k.aspx
SELECT
-- c => currency
-- n => numeric
FORMAT(987654321, N'N', C.culture) AS some_number
, FORMAT(987654321, N'c', C.culture) AS some_currency
, C.culture
FROM
(
-- Language culture names
-- http://msdn.microsoft.com/en-us/library/ee825488(v=cs.20).aspx
VALUES
('en-US')
, ('en-GB')
, ('ja-JP')
, ('Ro-RO')
, ('el-GR')
) C (culture);
SQLFiddle for the above
Demo 1
Demonstrates adding commas:
PRINT FORMATMESSAGE('The number is: %s', format(5000000, '#,##0'))
-- Output
The number is: 5,000,000
Demo 2
Demonstrates commas and decimal points. Observe that it rounds the last digit if necessary.
PRINT FORMATMESSAGE('The number is: %s', format(5000000.759145678, '#,##0.00'))
-- Output
The number is: 5,000,000.76
Compatibility
SQL Server 2012+.
Please try with below query:
SELECT FORMAT(987654321,'#,###,##0')
Format with right decimal point :
SELECT FORMAT(987654321,'#,###,##0.###\,###')
SELECT REPLACE(CONVERT(varchar(20), (CAST(9876543 AS money)), 1), '.00', '')
output= 9,876,543
and you can replace 9876543 by your column name.
Tried the money trick above, and this works great for numerical values with two or less significant digits. I created my own function to format numbers with decimals:
CREATE FUNCTION [dbo].[fn_FormatWithCommas]
(
-- Add the parameters for the function here
#value varchar(50)
)
RETURNS varchar(50)
AS
BEGIN
-- Declare the return variable here
DECLARE #WholeNumber varchar(50) = NULL, #Decimal varchar(10) = '', #CharIndex int = charindex('.', #value)
IF (#CharIndex > 0)
SELECT #WholeNumber = SUBSTRING(#value, 1, #CharIndex-1), #Decimal = SUBSTRING(#value, #CharIndex, LEN(#value))
ELSE
SET #WholeNumber = #value
IF(LEN(#WholeNumber) > 3)
SET #WholeNumber = dbo.fn_FormatWithCommas(SUBSTRING(#WholeNumber, 1, LEN(#WholeNumber)-3)) + ',' + RIGHT(#WholeNumber, 3)
-- Return the result of the function
RETURN #WholeNumber + #Decimal
END
This belongs in a comment to Phil Hunt's answer but alas I don't have the rep.
To strip the ".00" off the end of your number string, parsename is super-handy.
It tokenizes period-delimited strings and returns the specified element, starting with the rightmost token as element 1.
SELECT PARSENAME(CONVERT(varchar, CAST(987654321 AS money), 1), 2)
Yields "987,654,321"
here is another t-sql UDF
CREATE FUNCTION dbo.Format(#num int)
returns varChar(30)
As
Begin
Declare #out varChar(30) = ''
while #num > 0 Begin
Set #out = str(#num % 1000, 3, 0) + Coalesce(','+#out, '')
Set #num = #num / 1000
End
Return #out
End
`/* Author: Tsiridis Dimitris */
/* Greek amount format. For the other change the change on replace of '.' & ',' */
CREATE FUNCTION dbo.formatAmount (
#amtIn as varchar(20)
) RETURNS varchar(20)
AS
BEGIN
return cast(REPLACE(SUBSTRING(CONVERT(varchar(20), CAST(#amtIn AS money), 1),1,
LEN(CONVERT(varchar(20), CAST(#amtIn AS money), 1))-3), ',','.')
+ replace(RIGHT(CONVERT(varchar(20), CAST(#amtIn AS money), 1),3), '.',',') AS VARCHAR(20))
END
SELECT [geniki].[dbo].[formatAmount]('9888777666555.44')`
Here is a scalar function I am using that fixes some bugs in a previous example (above) and also handles decimal values (to the specified # of digits) (EDITED to also work with 0 & negative numbers). One other note, the cast as money method above is limited to the size of the MONEY data type, and doesn't work with 4 (or more) digits decimals. That method is definitely simpler but less flexible.
CREATE FUNCTION [dbo].[fnNumericWithCommas](#num decimal(38, 18), #decimals int = 4) RETURNS varchar(44) AS
BEGIN
DECLARE #ret varchar(44)
DECLARE #negative bit; SET #negative = CASE WHEN #num < 0 THEN 1 ELSE 0 END
SET #num = abs(round(#num, #decimals)) -- round the value to the number of decimals desired
DECLARE #decValue varchar(18); SET #decValue = substring(ltrim(#num - round(#num, 0, 1)) + '000000000000000000', 3, #decimals)
SET #num = round(#num, 0, 1) -- truncate the incoming number of any decimals
WHILE #num > 0 BEGIN
SET #ret = str(#num % 1000, 3, 0) + isnull(','+#ret, '')
SET #num = round(#num / 1000, 0, 1)
END
SET #ret = isnull(replace(ltrim(#ret), ' ', '0'), '0') + '.' + #decValue
IF (#negative = 1) SET #ret = '-' + #ret
RETURN #ret
END
GO
Another UDF which is hopefully generic enough and does not make assumptions about whether you want to round to a specific number of decimal places:
CREATE FUNCTION [dbo].[fn_FormatNumber] (#number decimal(38,18))
RETURNS varchar(50)
BEGIN
-- remove minus sign before applying thousands seperator
DECLARE #negative bit
SET #negative = CASE WHEN #number < 0 THEN 1 ELSE 0 END
SET #number = ABS(#number)
-- add thousands seperator for every 3 digits to the left of the decimal place
DECLARE #pos int, #result varchar(50) = CAST(#number AS varchar(50))
SELECT #pos = CHARINDEX('.', #result)
WHILE #pos > 4
BEGIN
SET #result = STUFF(#result, #pos-3, 0, ',')
SELECT #pos = CHARINDEX(',', #result)
END
-- remove trailing zeros
WHILE RIGHT(#result, 1) = '0'
SET #result = LEFT(#result, LEN(#result)-1)
-- remove decimal place if not required
IF RIGHT(#result, 1) = '.'
SET #result = LEFT(#result, LEN(#result)-1)
IF #negative = 1
SET #result = '-' + #result
RETURN #result
END
/*
#------------------------------------------------------------------------#
# SQL Query Script #
# ---------------- #
# Funcion.: dbo.fn_nDerecha ( Numero, Pos_Enteros, Pos_Decimales ) #
# Numero : es el Numero o Valor a formatear #
# Pos_Enteros : es la cantidad posiciones para Enteros #
# Pos_Decimales : es la cantidad posiciones para Decimales #
# #
# OBJETIVO: Formatear los Numeros con Coma y Justificado a la Derecha #
# Por Ejemplo: #
# dbo.fn_nDerecha ( Numero, 9, 2 ) Resultado = ---,---,--9.99 #
# dado Numero = 1234.56 Resultado = 1,234.56 #
# dado Numero = -1.56 Resultado = -1.56 #
# dado Numero = -53783423.56 Resultado = -53,783,423.56 #
# #
# Autor...: Francisco Eugenio Cabrera Perez #
# Fecha...: Noviembre 25, 2015 #
# Pais....: Republica Dominicana #
#------------------------------------------------------------------------#
*/
CREATE FUNCTION [dbo].[fn_nDerecha]
(
-- Agregue Argumentos, para personalizar la funcion a su conveniencia
#Numero_str varchar(max)
,#Pos_Enteros int
,#Pos_Decimales int
)
RETURNS varchar(max)
AS
BEGIN
-- Declare la variable del RETURN aqui, en este caso es RESULT
declare #RESULTADO varchar(max)
set #RESULTADO = '****'
----------------------------------------------- --
declare #Numero_num numeric(28,12)
set #Numero_num =
(
case when isnumeric(#Numero_str) = 0
then 0
else round (convert( numeric(28,12), #Numero_str), #Pos_Decimales)
end
)
-- ----------------------------------------------- --
-- Aumenta #Pos_Enteros de #RESULTADO,
-- si las posiciones de Enteros del dato #Numero_str es Mayor...
--
declare #Num_Pos_Ent int
set #Num_Pos_Ent = len ( convert( varchar, convert(int, abs(#Numero_num) ) ) )
--
declare #Pos_Ent_Mas int
set #Pos_Ent_Mas =
(
case when #Num_Pos_Ent > #Pos_Enteros
then #Num_Pos_Ent - #Pos_Enteros
else 0
end
)
set #Pos_Enteros = #Pos_Enteros + #Pos_Ent_Mas
--
-- ----------------------------------------------- --
declare #p_Signo_ctd int
set #p_Signo_ctd = (case when #Numero_num < 1 then 1 else 0 end)
--
declare #p_Comas_ctd int
set #p_Comas_ctd = ( #Pos_Enteros - 1 ) / 3
--
declare #p_Punto_ctd int
set #p_Punto_ctd = (case when #Pos_Decimales > 0 then 1 else 0 end)
--
declare #p_input_Longitud int
set #p_input_Longitud = ( #p_Signo_ctd + #Pos_Enteros ) +
#p_Punto_ctd + #Pos_Decimales
--
declare #p_output_Longitud int
set #p_output_Longitud = ( #p_Signo_ctd + #Pos_Enteros + #p_Comas_ctd )
+ ( #p_Punto_ctd + #Pos_Decimales )
--
-- =================================================================== --
declare #Valor_str varchar(max)
set #Valor_str = str(#Numero_num, #p_input_Longitud, #Pos_Decimales)
declare #V_Ent_str varchar(max)
set #V_Ent_str =
(case when #Pos_Decimales > 0
then substring( #Valor_str, 0, charindex('.', #Valor_str, 0) )
else #Valor_str end)
--
declare #V_Dec_str varchar(max)
set #V_Dec_str =
(case when #Pos_Decimales > 0
then '.' + right(#Valor_str, #Pos_Decimales)
else '' end)
--
set #V_Ent_str = convert(VARCHAR, convert(money, #V_Ent_str), 1)
set #V_Ent_str = substring( #V_Ent_str, 0, charindex('.', #V_Ent_str, 0) )
--
set #RESULTADO = #V_Ent_str + #V_Dec_str
--
set #RESULTADO = ( replicate( ' ', #p_output_Longitud - len(#RESULTADO) ) + #RESULTADO )
--
-- =================================================================== -
-- =================================================================== -
RETURN #RESULTADO
END
-- =================================================================== --
/*
This function needs 3 arguments: the First argument is the #Numero_str which the Number as data input, and the other 2 arguments specify how the information will be formatted for the output, those arguments are #Pos_Enteros and #Pos_Decimales which specify how many Integers and Decimal places you want to show for the Number you pass as input argument.
*/
For SQL Server before 2012 which does not include the FORMAT function, create this function:
CREATE FUNCTION FormatCurrency(#value numeric(30,2))
RETURNS varchar(50)
AS
BEGIN
DECLARE #NumAsChar VARCHAR(50)
SET #NumAsChar = '$' + CONVERT(varchar(50), CAST(#Value AS money),1)
RETURN #NumAsChar
END
select dbo.FormatCurrency(12345678)
returns $12,345,678.00
Drop the $ if you just want commas.

Return all words starting with a character in a column

I have a VARCHAR column with data like this:
abc = :abc and this = :that
I need a query to find all of the special "words" that start with a colon in this column of data. I don't really need any other data (IDs or otherwise) and duplicates would be OK. I can remove duplicates in Excel later if need be. So if this was the only row, I'd like something like this as the output:
SpecialWords
:abc
:that
I'm thinking it'll require a CHARINDEX or something like that. But since there could be more than one special word in the column, I can't just find the first : and strip out the rest.
Any help is greatly appreciated! Thanks in advance!
You have to split this value based on spaces and return only fields that starts with a colon :, i provided 2 solutions to achieve this based on the result type you need (Table or Single Value)
Table-Valued Function
You can create a TV function to split this column into a table:
CREATE FUNCTION [dbo].[GETVALUES]
(
#DelimitedString varchar(8000)
)
RETURNS #tblArray TABLE
(
ElementID int IDENTITY(1,1), -- Array index
Element varchar(1000) -- Array element contents
)
AS
BEGIN
-- Local Variable Declarations
-- ---------------------------
DECLARE #Index smallint,
#Start smallint,
#DelSize smallint
SET #DelSize = 1
-- Loop through source string and add elements to destination table array
-- ----------------------------------------------------------------------
WHILE LEN(#DelimitedString) > 0
BEGIN
SET #Index = CHARINDEX(' ', #DelimitedString)
IF #Index = 0
BEGIN
IF ((LTRIM(RTRIM(#DelimitedString))) LIKE ':%')
INSERT INTO
#tblArray
(Element)
VALUES
(LTRIM(RTRIM(#DelimitedString)))
BREAK
END
ELSE
BEGIN
IF (LTRIM(RTRIM(SUBSTRING(#DelimitedString, 1,#Index - 1)))) LIKE ':%'
INSERT INTO
#tblArray
(Element)
VALUES
(LTRIM(RTRIM(SUBSTRING(#DelimitedString, 1,#Index - 1))))
SET #Start = #Index + #DelSize
SET #DelimitedString = SUBSTRING(#DelimitedString, #Start , LEN(#DelimitedString) - #Start + 1)
END
END
RETURN
END
And you can use it like the following:
DECLARE #SQLStr varchar(100)
SELECT #SQLStr = 'abc = :abc and this = :that and xyz = :asd'
SELECT
*
FROM
dbo.GETVALUES(#SQLStr)
Result:
Scalar-Valued Function
If you need to return a value (not table) so you can use this function which will return on all values separated by (line feed + carridge return CHAR(13) + CHAR(10))
CREATE FUNCTION dbo.GetValues2
(
#DelimitedString varchar(8000)
)
RETURNS varchar(8000)
AS
BEGIN
DECLARE #Index smallint,
#Start smallint,
#DelSize smallint,
#Result varchar(8000)
SET #DelSize = 1
SET #Result = ''
WHILE LEN(#DelimitedString) > 0
BEGIN
SET #Index = CHARINDEX(' ', #DelimitedString)
IF #Index = 0
BEGIN
if (LTRIM(RTRIM(#DelimitedString))) LIKE ':%'
SET #Result = #Result + char(13) + char(10) + (LTRIM(RTRIM(#DelimitedString)))
BREAK
END
ELSE
BEGIN
IF (LTRIM(RTRIM(SUBSTRING(#DelimitedString, 1,#Index - 1)))) LIKE ':%'
SET #Result = #Result + char(13) + char(10) + (LTRIM(RTRIM(SUBSTRING(#DelimitedString, 1,#Index - 1))))
SET #Start = #Index + #DelSize
SET #DelimitedString = SUBSTRING(#DelimitedString, #Start , LEN(#DelimitedString) - #Start + 1)
END
END
return #Result
END
GO
you can use it as the following
DECLARE #SQLStr varchar(100)
SELECT #SQLStr = 'abc = :abc and this = :that and xyz = :asd'
SELECT dbo.GetValues2(#SQLStr)
Result
in the table result line feed are not visible, just copy the data to an editor and it will appears as shown in the image
References
Splitting the string in sql server
One way is to write a specialized SPLIT function. I would suggest getting a TSQL Split function off the internet and see if you can adapt the code to your needs.
Working from scratch, you could write a function that loops over the column value using CHARINDEX until it doesn't find any more : characters.
How about using a charindex?
rextester sample:
create table mytable (testcolumn varchar(20))
insert into mytable values ('this = :that'),('yes'), (':no'), ('abc = :abc')
select right(testcolumn, charindex(':', reverse(testcolumn)) - 1) from mytable
where testcolumn like '%:%'
reference:
SQL Select everything after character
Update
Addressing Sami's:
Didn't see that two words could be in one colon, how about this?
select replace(substring(testcolumn, charindex(':', testcolumn), len(testcolumn)), ':', '')
Update again
I see, the actual statement is this = :that and that = :this
If performance is important then you want to use an inline table valued function to split the string and extract what you need. You could use delimitedSplit8K or delimitedSplit8K_lead for this.
declare #string varchar(8000) = 'abc = :abc and this = :that';
select item
from dbo.DelimitedSplit8K(#string, ' ')
where item like ':%';
returns:
item
------
:abc
:that
And for even better performance than what I posted above you could use ngrams8k like so:
declare #string varchar(8000) = 'abc = :abc and this = :that';
select position, item =
substring(#string, position,
isnull(nullif(charindex(' ',#string,position+1),0),8000)-position)
from dbo.ngrams8k(#string, 1)
where token = ':';
This even gives you the location of the item you are searching for:
position item
---------- -------
7 :abc
23 :that

how to change format of amount number in sql? [duplicate]

I'm running some administrative queries and compiling results from sp_spaceused in SQL Server 2008 to look at data/index space ratios of some tables in my database. Of course I am getting all sorts of large numbers in the results and my eyes are starting to gloss over. It would be really convenient if I could format all those numbers with commas (987654321 becomes 987,654,321). Funny that in all the many years I've used SQL Server, this issue has never come up since most of the time I would be doing formatting at the presentation layer, but in this case the T-SQL result in SSMS is the presentation.
I've considered just creating a simple CLR UDF to solve this, but it seems like this should be do-able in just plain old T-SQL. So, I'll pose the question here - how do you do numeric formatting in vanilla T-SQL?
In SQL Server 2012 and higher, this will format a number with commas:
select format([Number], 'N0')
You can also change 0 to the number of decimal places you want.
While I agree with everyone, including the OP, who says that formatting should be done in the presentation layer, this formatting can be accomplished in T-SQL by casting to money and then converting to varchar. This does include trailing decimals, though, that could be looped off with SUBSTRING.
SELECT CONVERT(varchar, CAST(987654321 AS money), 1)
I'd recommend Replace in lieu of Substring to avoid string length issues:
REPLACE(CONVERT(varchar(20), (CAST(SUM(table.value) AS money)), 1), '.00', '')
For SQL Server 2012+ implementations, you will have the ability to use the FORMAT to apply string formatting to non-string data types.
In the original question, the user had requested the ability to use commas as thousands separators. In a closed as duplicate question, the user had asked how they could apply currency formatting. The following query shows how to perform both tasks. It also demonstrates the application of culture to make this a more generic solution (addressing Tsiridis Dimitris's function to apply Greek special formatting)
-- FORMAT
-- http://msdn.microsoft.com/en-us/library/hh213505(v=sql.110).aspx
-- FORMAT does not do conversion, that's the domain of cast/convert/parse etc
-- Only accepts numeric and date/time data types for formatting.
--
-- Formatting Types
-- http://msdn.microsoft.com/en-us/library/26etazsy.aspx
-- Standard numeric format strings
-- http://msdn.microsoft.com/en-us/library/dwhawy9k.aspx
SELECT
-- c => currency
-- n => numeric
FORMAT(987654321, N'N', C.culture) AS some_number
, FORMAT(987654321, N'c', C.culture) AS some_currency
, C.culture
FROM
(
-- Language culture names
-- http://msdn.microsoft.com/en-us/library/ee825488(v=cs.20).aspx
VALUES
('en-US')
, ('en-GB')
, ('ja-JP')
, ('Ro-RO')
, ('el-GR')
) C (culture);
SQLFiddle for the above
Demo 1
Demonstrates adding commas:
PRINT FORMATMESSAGE('The number is: %s', format(5000000, '#,##0'))
-- Output
The number is: 5,000,000
Demo 2
Demonstrates commas and decimal points. Observe that it rounds the last digit if necessary.
PRINT FORMATMESSAGE('The number is: %s', format(5000000.759145678, '#,##0.00'))
-- Output
The number is: 5,000,000.76
Compatibility
SQL Server 2012+.
Please try with below query:
SELECT FORMAT(987654321,'#,###,##0')
Format with right decimal point :
SELECT FORMAT(987654321,'#,###,##0.###\,###')
SELECT REPLACE(CONVERT(varchar(20), (CAST(9876543 AS money)), 1), '.00', '')
output= 9,876,543
and you can replace 9876543 by your column name.
Tried the money trick above, and this works great for numerical values with two or less significant digits. I created my own function to format numbers with decimals:
CREATE FUNCTION [dbo].[fn_FormatWithCommas]
(
-- Add the parameters for the function here
#value varchar(50)
)
RETURNS varchar(50)
AS
BEGIN
-- Declare the return variable here
DECLARE #WholeNumber varchar(50) = NULL, #Decimal varchar(10) = '', #CharIndex int = charindex('.', #value)
IF (#CharIndex > 0)
SELECT #WholeNumber = SUBSTRING(#value, 1, #CharIndex-1), #Decimal = SUBSTRING(#value, #CharIndex, LEN(#value))
ELSE
SET #WholeNumber = #value
IF(LEN(#WholeNumber) > 3)
SET #WholeNumber = dbo.fn_FormatWithCommas(SUBSTRING(#WholeNumber, 1, LEN(#WholeNumber)-3)) + ',' + RIGHT(#WholeNumber, 3)
-- Return the result of the function
RETURN #WholeNumber + #Decimal
END
This belongs in a comment to Phil Hunt's answer but alas I don't have the rep.
To strip the ".00" off the end of your number string, parsename is super-handy.
It tokenizes period-delimited strings and returns the specified element, starting with the rightmost token as element 1.
SELECT PARSENAME(CONVERT(varchar, CAST(987654321 AS money), 1), 2)
Yields "987,654,321"
here is another t-sql UDF
CREATE FUNCTION dbo.Format(#num int)
returns varChar(30)
As
Begin
Declare #out varChar(30) = ''
while #num > 0 Begin
Set #out = str(#num % 1000, 3, 0) + Coalesce(','+#out, '')
Set #num = #num / 1000
End
Return #out
End
`/* Author: Tsiridis Dimitris */
/* Greek amount format. For the other change the change on replace of '.' & ',' */
CREATE FUNCTION dbo.formatAmount (
#amtIn as varchar(20)
) RETURNS varchar(20)
AS
BEGIN
return cast(REPLACE(SUBSTRING(CONVERT(varchar(20), CAST(#amtIn AS money), 1),1,
LEN(CONVERT(varchar(20), CAST(#amtIn AS money), 1))-3), ',','.')
+ replace(RIGHT(CONVERT(varchar(20), CAST(#amtIn AS money), 1),3), '.',',') AS VARCHAR(20))
END
SELECT [geniki].[dbo].[formatAmount]('9888777666555.44')`
Here is a scalar function I am using that fixes some bugs in a previous example (above) and also handles decimal values (to the specified # of digits) (EDITED to also work with 0 & negative numbers). One other note, the cast as money method above is limited to the size of the MONEY data type, and doesn't work with 4 (or more) digits decimals. That method is definitely simpler but less flexible.
CREATE FUNCTION [dbo].[fnNumericWithCommas](#num decimal(38, 18), #decimals int = 4) RETURNS varchar(44) AS
BEGIN
DECLARE #ret varchar(44)
DECLARE #negative bit; SET #negative = CASE WHEN #num < 0 THEN 1 ELSE 0 END
SET #num = abs(round(#num, #decimals)) -- round the value to the number of decimals desired
DECLARE #decValue varchar(18); SET #decValue = substring(ltrim(#num - round(#num, 0, 1)) + '000000000000000000', 3, #decimals)
SET #num = round(#num, 0, 1) -- truncate the incoming number of any decimals
WHILE #num > 0 BEGIN
SET #ret = str(#num % 1000, 3, 0) + isnull(','+#ret, '')
SET #num = round(#num / 1000, 0, 1)
END
SET #ret = isnull(replace(ltrim(#ret), ' ', '0'), '0') + '.' + #decValue
IF (#negative = 1) SET #ret = '-' + #ret
RETURN #ret
END
GO
Another UDF which is hopefully generic enough and does not make assumptions about whether you want to round to a specific number of decimal places:
CREATE FUNCTION [dbo].[fn_FormatNumber] (#number decimal(38,18))
RETURNS varchar(50)
BEGIN
-- remove minus sign before applying thousands seperator
DECLARE #negative bit
SET #negative = CASE WHEN #number < 0 THEN 1 ELSE 0 END
SET #number = ABS(#number)
-- add thousands seperator for every 3 digits to the left of the decimal place
DECLARE #pos int, #result varchar(50) = CAST(#number AS varchar(50))
SELECT #pos = CHARINDEX('.', #result)
WHILE #pos > 4
BEGIN
SET #result = STUFF(#result, #pos-3, 0, ',')
SELECT #pos = CHARINDEX(',', #result)
END
-- remove trailing zeros
WHILE RIGHT(#result, 1) = '0'
SET #result = LEFT(#result, LEN(#result)-1)
-- remove decimal place if not required
IF RIGHT(#result, 1) = '.'
SET #result = LEFT(#result, LEN(#result)-1)
IF #negative = 1
SET #result = '-' + #result
RETURN #result
END
/*
#------------------------------------------------------------------------#
# SQL Query Script #
# ---------------- #
# Funcion.: dbo.fn_nDerecha ( Numero, Pos_Enteros, Pos_Decimales ) #
# Numero : es el Numero o Valor a formatear #
# Pos_Enteros : es la cantidad posiciones para Enteros #
# Pos_Decimales : es la cantidad posiciones para Decimales #
# #
# OBJETIVO: Formatear los Numeros con Coma y Justificado a la Derecha #
# Por Ejemplo: #
# dbo.fn_nDerecha ( Numero, 9, 2 ) Resultado = ---,---,--9.99 #
# dado Numero = 1234.56 Resultado = 1,234.56 #
# dado Numero = -1.56 Resultado = -1.56 #
# dado Numero = -53783423.56 Resultado = -53,783,423.56 #
# #
# Autor...: Francisco Eugenio Cabrera Perez #
# Fecha...: Noviembre 25, 2015 #
# Pais....: Republica Dominicana #
#------------------------------------------------------------------------#
*/
CREATE FUNCTION [dbo].[fn_nDerecha]
(
-- Agregue Argumentos, para personalizar la funcion a su conveniencia
#Numero_str varchar(max)
,#Pos_Enteros int
,#Pos_Decimales int
)
RETURNS varchar(max)
AS
BEGIN
-- Declare la variable del RETURN aqui, en este caso es RESULT
declare #RESULTADO varchar(max)
set #RESULTADO = '****'
----------------------------------------------- --
declare #Numero_num numeric(28,12)
set #Numero_num =
(
case when isnumeric(#Numero_str) = 0
then 0
else round (convert( numeric(28,12), #Numero_str), #Pos_Decimales)
end
)
-- ----------------------------------------------- --
-- Aumenta #Pos_Enteros de #RESULTADO,
-- si las posiciones de Enteros del dato #Numero_str es Mayor...
--
declare #Num_Pos_Ent int
set #Num_Pos_Ent = len ( convert( varchar, convert(int, abs(#Numero_num) ) ) )
--
declare #Pos_Ent_Mas int
set #Pos_Ent_Mas =
(
case when #Num_Pos_Ent > #Pos_Enteros
then #Num_Pos_Ent - #Pos_Enteros
else 0
end
)
set #Pos_Enteros = #Pos_Enteros + #Pos_Ent_Mas
--
-- ----------------------------------------------- --
declare #p_Signo_ctd int
set #p_Signo_ctd = (case when #Numero_num < 1 then 1 else 0 end)
--
declare #p_Comas_ctd int
set #p_Comas_ctd = ( #Pos_Enteros - 1 ) / 3
--
declare #p_Punto_ctd int
set #p_Punto_ctd = (case when #Pos_Decimales > 0 then 1 else 0 end)
--
declare #p_input_Longitud int
set #p_input_Longitud = ( #p_Signo_ctd + #Pos_Enteros ) +
#p_Punto_ctd + #Pos_Decimales
--
declare #p_output_Longitud int
set #p_output_Longitud = ( #p_Signo_ctd + #Pos_Enteros + #p_Comas_ctd )
+ ( #p_Punto_ctd + #Pos_Decimales )
--
-- =================================================================== --
declare #Valor_str varchar(max)
set #Valor_str = str(#Numero_num, #p_input_Longitud, #Pos_Decimales)
declare #V_Ent_str varchar(max)
set #V_Ent_str =
(case when #Pos_Decimales > 0
then substring( #Valor_str, 0, charindex('.', #Valor_str, 0) )
else #Valor_str end)
--
declare #V_Dec_str varchar(max)
set #V_Dec_str =
(case when #Pos_Decimales > 0
then '.' + right(#Valor_str, #Pos_Decimales)
else '' end)
--
set #V_Ent_str = convert(VARCHAR, convert(money, #V_Ent_str), 1)
set #V_Ent_str = substring( #V_Ent_str, 0, charindex('.', #V_Ent_str, 0) )
--
set #RESULTADO = #V_Ent_str + #V_Dec_str
--
set #RESULTADO = ( replicate( ' ', #p_output_Longitud - len(#RESULTADO) ) + #RESULTADO )
--
-- =================================================================== -
-- =================================================================== -
RETURN #RESULTADO
END
-- =================================================================== --
/*
This function needs 3 arguments: the First argument is the #Numero_str which the Number as data input, and the other 2 arguments specify how the information will be formatted for the output, those arguments are #Pos_Enteros and #Pos_Decimales which specify how many Integers and Decimal places you want to show for the Number you pass as input argument.
*/
For SQL Server before 2012 which does not include the FORMAT function, create this function:
CREATE FUNCTION FormatCurrency(#value numeric(30,2))
RETURNS varchar(50)
AS
BEGIN
DECLARE #NumAsChar VARCHAR(50)
SET #NumAsChar = '$' + CONVERT(varchar(50), CAST(#Value AS money),1)
RETURN #NumAsChar
END
select dbo.FormatCurrency(12345678)
returns $12,345,678.00
Drop the $ if you just want commas.

Query to get only numbers from a string

I have data like this:
string 1: 003Preliminary Examination Plan
string 2: Coordination005
string 3: Balance1000sheet
The output I expect is
string 1: 003
string 2: 005
string 3: 1000
And I want to implement it in SQL.
First create this UDF
CREATE FUNCTION dbo.udf_GetNumeric
(
#strAlphaNumeric VARCHAR(256)
)
RETURNS VARCHAR(256)
AS
BEGIN
DECLARE #intAlpha INT
SET #intAlpha = PATINDEX('%[^0-9]%', #strAlphaNumeric)
BEGIN
WHILE #intAlpha > 0
BEGIN
SET #strAlphaNumeric = STUFF(#strAlphaNumeric, #intAlpha, 1, '' )
SET #intAlpha = PATINDEX('%[^0-9]%', #strAlphaNumeric )
END
END
RETURN ISNULL(#strAlphaNumeric,0)
END
GO
Now use the function as
SELECT dbo.udf_GetNumeric(column_name)
from table_name
SQL FIDDLE
I hope this solved your problem.
Reference
Try this one -
Query:
DECLARE #temp TABLE
(
string NVARCHAR(50)
)
INSERT INTO #temp (string)
VALUES
('003Preliminary Examination Plan'),
('Coordination005'),
('Balance1000sheet')
SELECT LEFT(subsrt, PATINDEX('%[^0-9]%', subsrt + 't') - 1)
FROM (
SELECT subsrt = SUBSTRING(string, pos, LEN(string))
FROM (
SELECT string, pos = PATINDEX('%[0-9]%', string)
FROM #temp
) d
) t
Output:
----------
003
005
1000
Query:
DECLARE #temp TABLE
(
string NVARCHAR(50)
)
INSERT INTO #temp (string)
VALUES
('003Preliminary Examination Plan'),
('Coordination005'),
('Balance1000sheet')
SELECT SUBSTRING(string, PATINDEX('%[0-9]%', string), PATINDEX('%[0-9][^0-9]%', string + 't') - PATINDEX('%[0-9]%',
string) + 1) AS Number
FROM #temp
Please try:
declare #var nvarchar(max)='Balance1000sheet'
SELECT LEFT(Val,PATINDEX('%[^0-9]%', Val+'a')-1) from(
SELECT SUBSTRING(#var, PATINDEX('%[0-9]%', #var), LEN(#var)) Val
)x
Getting only numbers from a string can be done in a one-liner.
Try this :
SUBSTRING('your-string-here', PATINDEX('%[0-9]%', 'your-string-here'), LEN('your-string-here'))
NB: Only works for the first int in the string, ex: abc123vfg34 returns 123.
I found this approach works about 3x faster than the top voted answer. Create the following function, dbo.GetNumbers:
CREATE FUNCTION dbo.GetNumbers(#String VARCHAR(8000))
RETURNS VARCHAR(8000)
AS
BEGIN;
WITH
Numbers
AS (
--Step 1.
--Get a column of numbers to represent
--every character position in the #String.
SELECT 1 AS Number
UNION ALL
SELECT Number + 1
FROM Numbers
WHERE Number < LEN(#String)
)
,Characters
AS (
SELECT Character
FROM Numbers
CROSS APPLY (
--Step 2.
--Use the column of numbers generated above
--to tell substring which character to extract.
SELECT SUBSTRING(#String, Number, 1) AS Character
) AS c
)
--Step 3.
--Pattern match to return only numbers from the CTE
--and use STRING_AGG to rebuild it into a single string.
SELECT #String = STRING_AGG(Character,'')
FROM Characters
WHERE Character LIKE '[0-9]'
--allows going past the default maximum of 100 loops in the CTE
OPTION (MAXRECURSION 8000)
RETURN #String
END
GO
Testing
Testing for purpose:
SELECT dbo.GetNumbers(InputString) AS Numbers
FROM ( VALUES
('003Preliminary Examination Plan') --output: 003
,('Coordination005') --output: 005
,('Balance1000sheet') --output: 1000
,('(111) 222-3333') --output: 1112223333
,('1.38hello#f00.b4r#\-6') --output: 1380046
) testData(InputString)
Testing for performance:
Start off setting up the test data...
--Add table to hold test data
CREATE TABLE dbo.NumTest (String VARCHAR(8000))
--Make an 8000 character string with mix of numbers and letters
DECLARE #Num VARCHAR(8000) = REPLICATE('12tf56se',800)
--Add this to the test table 500 times
DECLARE #n INT = 0
WHILE #n < 500
BEGIN
INSERT INTO dbo.NumTest VALUES (#Num)
SET #n = #n +1
END
Now testing the dbo.GetNumbers function:
SELECT dbo.GetNumbers(NumTest.String) AS Numbers
FROM dbo.NumTest -- Time to complete: 1 min 7s
Then testing the UDF from the top voted answer on the same data.
SELECT dbo.udf_GetNumeric(NumTest.String)
FROM dbo.NumTest -- Time to complete: 3 mins 12s
Inspiration for dbo.GetNumbers
Decimals
If you need it to handle decimals, you can use either of the following approaches, I found no noticeable performance differences between them.
change '[0-9]' to '[0-9.]'
change Character LIKE '[0-9]' to ISNUMERIC(Character) = 1 (SQL treats a single decimal point as "numeric")
Bonus
You can easily adapt this to differing requirements by swapping out WHERE Character LIKE '[0-9]' with the following options:
WHERE Letter LIKE '[a-zA-Z]' --Get only letters
WHERE Letter LIKE '[0-9a-zA-Z]' --Remove non-alphanumeric
WHERE Letter LIKE '[^0-9a-zA-Z]' --Get only non-alphanumeric
With the previous queries I get these results:
'AAAA1234BBBB3333' >>>> Output: 1234
'-çã+0!\aº1234' >>>> Output: 0
The code below returns All numeric chars:
1st output: 12343333
2nd output: 01234
declare #StringAlphaNum varchar(255)
declare #Character varchar
declare #SizeStringAlfaNumerica int
declare #CountCharacter int
set #StringAlphaNum = 'AAAA1234BBBB3333'
set #SizeStringAlfaNumerica = len(#StringAlphaNum)
set #CountCharacter = 1
while isnumeric(#StringAlphaNum) = 0
begin
while #CountCharacter < #SizeStringAlfaNumerica
begin
if substring(#StringAlphaNum,#CountCharacter,1) not like '[0-9]%'
begin
set #Character = substring(#StringAlphaNum,#CountCharacter,1)
set #StringAlphaNum = replace(#StringAlphaNum, #Character, '')
end
set #CountCharacter = #CountCharacter + 1
end
set #CountCharacter = 0
end
select #StringAlphaNum
declare #puvodni nvarchar(20)
set #puvodni = N'abc1d8e8ttr987avc'
WHILE PATINDEX('%[^0-9]%', #puvodni) > 0 SET #puvodni = REPLACE(#puvodni, SUBSTRING(#puvodni, PATINDEX('%[^0-9]%', #puvodni), 1), '' )
SELECT #puvodni
A solution for SQL Server 2017 and later, using TRANSLATE:
DECLARE #T table (string varchar(50) NOT NULL);
INSERT #T
(string)
VALUES
('003Preliminary Examination Plan'),
('Coordination005'),
('Balance1000sheet');
SELECT
result =
REPLACE(
TRANSLATE(
T.string COLLATE Latin1_General_CI_AI,
'abcdefghijklmnopqrstuvwxyz',
SPACE(26)),
SPACE(1),
SPACE(0))
FROM #T AS T;
Output:
result
003
005
1000
The code works by:
Replacing characters a-z (ignoring case & accents) with a space
Replacing spaces with an empty string.
The string supplied to TRANSLATE can be expanded to include additional characters.
I did not have rights to create functions but had text like
["blahblah012345679"]
And needed to extract the numbers out of the middle
Note this assumes the numbers are grouped together and not at the start and end of the string.
select substring(column_name,patindex('%[0-9]%', column_name),patindex('%[0-9][^0-9]%', column_name)-patindex('%[0-9]%', column_name)+1)
from table name
Although this is an old thread its the first in google search, I came up with a different answer than what came before. This will allow you to pass your criteria for what to keep within a string, whatever that criteria might be. You can put it in a function to call over and over again if you want.
declare #String VARCHAR(MAX) = '-123. a 456-78(90)'
declare #MatchExpression VARCHAR(255) = '%[0-9]%'
declare #return varchar(max)
WHILE PatIndex(#MatchExpression, #String) > 0
begin
set #return = CONCAT(#return, SUBSTRING(#string,patindex(#matchexpression, #string),1))
SET #String = Stuff(#String, PatIndex(#MatchExpression, #String), 1, '')
end
select (#return)
This UDF will work for all types of strings:
CREATE FUNCTION udf_getNumbersFromString (#string varchar(max))
RETURNS varchar(max)
AS
BEGIN
WHILE #String like '%[^0-9]%'
SET #String = REPLACE(#String, SUBSTRING(#String, PATINDEX('%[^0-9]%', #String), 1), '')
RETURN #String
END
Just a little modification to #Epsicron 's answer
SELECT SUBSTRING(string, PATINDEX('%[0-9]%', string), PATINDEX('%[0-9][^0-9]%', string + 't') - PATINDEX('%[0-9]%',
string) + 1) AS Number
FROM (values ('003Preliminary Examination Plan'),
('Coordination005'),
('Balance1000sheet')) as a(string)
no need for a temporary variable
Firstly find out the number's starting length then reverse the string to find out the first position again(which will give you end position of number from the end). Now if you deduct 1 from both number and deduct it from string whole length you'll get only number length. Now get the number using SUBSTRING
declare #fieldName nvarchar(100)='AAAA1221.121BBBB'
declare #lenSt int=(select PATINDEX('%[0-9]%', #fieldName)-1)
declare #lenEnd int=(select PATINDEX('%[0-9]%', REVERSE(#fieldName))-1)
select SUBSTRING(#fieldName, PATINDEX('%[0-9]%', #fieldName), (LEN(#fieldName) - #lenSt -#lenEnd))
T-SQL function to read all the integers from text and return the one at the indicated index, starting from left or right, also using a starting search term (optional):
create or alter function dbo.udf_number_from_text(
#text nvarchar(max),
#search_term nvarchar(1000) = N'',
#number_position tinyint = 1,
#rtl bit = 0
) returns int
as
begin
declare #result int = 0;
declare #search_term_index int = 0;
if #text is null or len(#text) = 0 goto exit_label;
set #text = trim(#text);
if len(#text) = len(#search_term) goto exit_label;
if len(#search_term) > 0
begin
set #search_term_index = charindex(#search_term, #text);
if #search_term_index = 0 goto exit_label;
end;
if #search_term_index > 0
if #rtl = 0
set #text = trim(right(#text, len(#text) - #search_term_index - len(#search_term) + 1));
else
set #text = trim(left(#text, #search_term_index - 1));
if len(#text) = 0 goto exit_label;
declare #patt_number nvarchar(10) = '%[0-9]%';
declare #patt_not_number nvarchar(10) = '%[^0-9]%';
declare #number_start int = 1;
declare #number_end int;
declare #found_numbers table (id int identity(1,1), val int);
while #number_start > 0
begin
set #number_start = patindex(#patt_number, #text);
if #number_start > 0
begin
if #number_start = len(#text)
begin
insert into #found_numbers(val)
select cast(substring(#text, #number_start, 1) as int);
break;
end;
else
begin
set #text = right(#text, len(#text) - #number_start + 1);
set #number_end = patindex(#patt_not_number, #text);
if #number_end = 0
begin
insert into #found_numbers(val)
select cast(#text as int);
break;
end;
else
begin
insert into #found_numbers(val)
select cast(left(#text, #number_end - 1) as int);
if #number_end = len(#text)
break;
else
begin
set #text = trim(right(#text, len(#text) - #number_end));
if len(#text) = 0 break;
end;
end;
end;
end;
end;
if #rtl = 0
select #result = coalesce(a.val, 0)
from (select row_number() over (order by m.id asc) as c_row, m.val
from #found_numbers as m) as a
where a.c_row = #number_position;
else
select #result = coalesce(a.val, 0)
from (select row_number() over (order by m.id desc) as c_row, m.val
from #found_numbers as m) as a
where a.c_row = #number_position;
exit_label:
return #result;
end;
Example:
select dbo.udf_number_from text(N'Text text 10 text, 25 term', N'term',2,1);
returns 10;
This is one of the simplest and easiest one. This will work on the entire String for multiple occurences as well.
CREATE FUNCTION dbo.fn_GetNumbers(#strInput NVARCHAR(500))
RETURNS NVARCHAR(500)
AS
BEGIN
DECLARE #strOut NVARCHAR(500) = '', #intCounter INT = 1
WHILE #intCounter <= LEN(#strInput)
BEGIN
SELECT #strOut = #strOut + CASE WHEN SUBSTRING(#strInput, #intCounter, 1) LIKE '[0-9]' THEN SUBSTRING(#strInput, #intCounter, 1) ELSE '' END
SET #intCounter = #intCounter + 1
END
RETURN #strOut
END
Following a solution using a single common table expression (CTE).
DECLARE #s AS TABLE (id int PRIMARY KEY, value nvarchar(max));
INSERT INTO #s
VALUES
(1, N'003Preliminary Examination Plan'),
(2, N'Coordination005'),
(3, N'Balance1000sheet');
SELECT * FROM #s ORDER BY id;
WITH t AS (
SELECT
id,
1 AS i,
SUBSTRING(value, 1, 1) AS c
FROM
#s
WHERE
LEN(value) > 0
UNION ALL
SELECT
t.id,
t.i + 1 AS i,
SUBSTRING(s.value, t.i + 1, 1) AS c
FROM
t
JOIN #s AS s ON t.id = s.id
WHERE
t.i < LEN(s.value)
)
SELECT
id,
STRING_AGG(c, N'') WITHIN GROUP (ORDER BY i ASC) AS value
FROM
t
WHERE
c LIKE '[0-9]'
GROUP BY
id
ORDER BY
id;
DECLARE #index NVARCHAR(20);
SET #index = 'abd565klaf12';
WHILE PATINDEX('%[0-9]%', #index) != 0
BEGIN
SET #index = REPLACE(#index, SUBSTRING(#index, PATINDEX('%[0-9]%', #index), 1), '');
END
SELECT #index;
One can replace [0-9] with [a-z] if numbers only are wanted with desired castings using the CAST function.
If we use the User Define Function, the query speed will be greatly reduced. This code extracts the number from the string....
SELECT
Reverse(substring(Reverse(rtrim(ltrim( substring([FieldName] , patindex('%[0-9]%', [FieldName] ) , len([FieldName]) )))) , patindex('%[0-9]%', Reverse(rtrim(ltrim( substring([FieldName] , patindex('%[0-9]%', [FieldName] ) , len([FieldName]) )))) ), len(Reverse(rtrim(ltrim( substring([FieldName] , patindex('%[0-9]%', [FieldName] ) , len([FieldName]) ))))) )) NumberValue
FROM dbo.TableName
CREATE OR REPLACE FUNCTION count_letters_and_numbers(input_string TEXT)
RETURNS TABLE (letters INT, numbers INT) AS $$
BEGIN
RETURN QUERY SELECT
sum(CASE WHEN input_string ~ '[A-Za-z]' THEN 1 ELSE 0 END) as letters,
sum(CASE WHEN input_string ~ '[0-9]' THEN 1 ELSE 0 END) as numbers
FROM unnest(string_to_array(input_string, '')) as input_string;
END;
$$ LANGUAGE plpgsql;
For the hell of it...
This solution is different to all earlier solutions, viz:
There is no need to create a function
There is no need to use pattern matching
There is no need for a temporary table
This solution uses a recursive common table expression (CTE)
But first - note the question does not specify where such strings are stored. In my solution below, I create a CTE as a quick and dirty way to put these strings into some kind of "source table".
Note also - this solution uses a recursive common table expression (CTE) - so don't get confused by the usage of two CTEs here. The first is simply to make the data avaliable to the solution - but it is only the second CTE that is required in order to solve this problem. You can adapt the code to make this second CTE query your existing table, view, etc.
Lastly - my coding is verbose, trying to use column and CTE names that explain what is going on and you might be able to simplify this solution a little. I've added in a few pseudo phone numbers with some (expected and atypical, as the case may be) formatting for the fun of it.
with SOURCE_TABLE as (
select '003Preliminary Examination Plan' as numberString
union all select 'Coordination005' as numberString
union all select 'Balance1000sheet' as numberString
union all select '1300 456 678' as numberString
union all select '(012) 995 8322 ' as numberString
union all select '073263 6122,' as numberString
),
FIRST_CHAR_PROCESSED as (
select
len(numberString) as currentStringLength,
isNull(cast(try_cast(replace(left(numberString, 1),' ','z') as tinyint) as nvarchar),'') as firstCharAsNumeric,
cast(isNull(cast(try_cast(nullIf(left(numberString, 1),'') as tinyint) as nvarchar),'') as nvarchar(4000)) as newString,
cast(substring(numberString,2,len(numberString)) as nvarchar) as remainingString
from SOURCE_TABLE
union all
select
len(remainingString) as currentStringLength,
cast(try_cast(replace(left(remainingString, 1),' ','z') as tinyint) as nvarchar) as firstCharAsNumeric,
cast(isNull(newString,'') as nvarchar(3999)) + isNull(cast(try_cast(nullIf(left(remainingString, 1),'') as tinyint) as nvarchar(1)),'') as newString,
substring(remainingString,2,len(remainingString)) as remainingString
from FIRST_CHAR_PROCESSED fcp2
where fcp2.currentStringLength > 1
)
select
newString
,* -- comment this out when required
from FIRST_CHAR_PROCESSED
where currentStringLength = 1
So what's going on here?
Basically in our CTE we are selecting the first character and using try_cast (see docs) to cast it to a tinyint (which is a large enough data type for a single-digit numeral). Note that the type-casting rules in SQL Server say that an empty string (or a space, for that matter) will resolve to zero, so the nullif is added to force spaces and empty strings to resolve to null (see discussion) (otherwise our result would include a zero character any time a space is encountered in the source data).
The CTE also returns everything after the first character - and that becomes the input to our recursive call on the CTE; in other words: now let's process the next character.
Lastly, the field newString in the CTE is generated (in the second SELECT) via concatenation. With recursive CTEs the data type must match between the two SELECT statements for any given column - including the column size. Because we know we are adding (at most) a single character, we are casting that character to nvarchar(1) and we are casting the newString (so far) as nvarchar(3999). Concatenated, the result will be nvarchar(4000) - which matches the type casting we carry out in the first SELECT.
If you run this query and exclude the WHERE clause, you'll get a sense of what's going on - but the rows may be in a strange order. (You won't necessarily see all rows relating to a single input value grouped together - but you should still be able to follow).
Hope it's an interesting option that may help a few people wanting a strictly expression-based solution.
In Oracle
You can get what you want using this:
SUBSTR('ABCD1234EFGH',REGEXP_INSTR ('ABCD1234EFGH', '[[:digit:]]'),REGEXP_COUNT ('ABCD1234EFGH', '[[:digit:]]'))
Sample Query:
SELECT SUBSTR('003Preliminary Examination Plan ',REGEXP_INSTR ('003Preliminary Examination Plan ', '[[:digit:]]'),REGEXP_COUNT ('003Preliminary Examination Plan ', '[[:digit:]]')) SAMPLE1,
SUBSTR('Coordination005',REGEXP_INSTR ('Coordination005', '[[:digit:]]'),REGEXP_COUNT ('Coordination005', '[[:digit:]]')) SAMPLE2,
SUBSTR('Balance1000sheet',REGEXP_INSTR ('Balance1000sheet', '[[:digit:]]'),REGEXP_COUNT ('Balance1000sheet', '[[:digit:]]')) SAMPLE3 FROM DUAL
If you are using Postgres and you have data like '2000 - some sample text' then try substring and position combination, otherwise if in your scenario there is no delimiter, you need to write regex:
SUBSTRING(Column_name from 0 for POSITION('-' in column_name) - 1) as
number_column_name

T-SQL trim &nbsp (and other non-alphanumeric characters)

We have some input data that sometimes appears with &nbsp characters on the end.
The data comes in from the source system as varchar() and our attempts to cast as decimal fail b/c of these characters.
Ltrim and Rtrim don't remove the characters, so we're forced to do something like:
UPDATE myTable
SET myColumn = replace(myColumn,char(160),'')
WHERE charindex(char(160),myColumn) > 0
This works for the &nbsp, but is there a good way to do this for any non-alphanumeric (or in this case numeric) characters?
This will remove all non alphanumeric chracters
CREATE FUNCTION [dbo].[fnRemoveBadCharacter]
(
#BadString nvarchar(20)
)
RETURNS nvarchar(20)
AS
BEGIN
DECLARE #nPos INTEGER
SELECT #nPos = PATINDEX('%[^a-zA-Z0-9_]%', #BadString)
WHILE #nPos > 0
BEGIN
SELECT #BadString = STUFF(#BadString, #nPos, 1, '')
SELECT #nPos = PATINDEX('%[^a-zA-Z0-9_]%', #BadString)
END
RETURN #BadString
END
Use the function like:
UPDATE TableToUpdate
SET ColumnToUpdate = dbo.fnRemoveBadCharacter(ColumnToUpdate)
WHERE whatever
This page has a sample of how you can remove non-alphanumeric chars:
-- Put something like this into a user function:
DECLARE #cString VARCHAR(32)
DECLARE #nPos INTEGER
SELECT #cString = '90$%45623 *6%}~:#'
SELECT #nPos = PATINDEX('%[^0-9]%', #cString)
WHILE #nPos > 0
BEGIN
SELECT #cString = STUFF(#cString, #nPos, 1, '')
SELECT #nPos = PATINDEX('%[^0-9]%', #cString)
END
SELECT #cString
How is the table being populated? While it is possible to scrub this in sql a better approach would be to change the column type to int and scrub the data before it's loaded into the database (SSIS). Is this an option?
For large datasets I have had better luck with this function that checks the ASCII value. I have added options to keep only alpha, numeric or alphanumeric based on the parameters.
--CleanType 1 - Remove all non alpanumeric
-- 2 - Remove only alpha
-- 3 - Remove only numeric
CREATE FUNCTION [dbo].[fnCleanString] (
#InputString varchar(8000)
, #CleanType int
, #LeaveSpaces bit
) RETURNS varchar(8000)
AS
BEGIN
-- // Declare variables
-- ===========================================================
DECLARE #Length int
, #CurLength int = 1
, #ReturnString varchar(8000)=''
SELECT #Length = len(#InputString)
-- // Begin looping through each char checking ASCII value
-- ===========================================================
WHILE (#CurLength <= (#Length+1))
BEGIN
IF (ASCII(SUBSTRING(#InputString,#CurLength,1)) between 48 and 57 AND #CleanType in (1,3) )
or (ASCII(SUBSTRING(#InputString,#CurLength,1)) between 65 and 90 AND #CleanType in (1,2) )
or (ASCII(SUBSTRING(#InputString,#CurLength,1)) between 97 and 122 AND #CleanType in (1,2) )
or (ASCII(SUBSTRING(#InputString,#CurLength,1)) = 32 AND #LeaveSpaces = 1 )
BEGIN
SET #ReturnString = #ReturnString + SUBSTRING(#InputString,#CurLength,1)
END
SET #CurLength = #CurLength + 1
END
RETURN #ReturnString
END
If the mobile could start with a Plus(+) I will use the function like this
CREATE FUNCTION [dbo].[Mobile_NoAlpha](#Mobile VARCHAR(1000))
RETURNS VARCHAR(1000)
AS
BEGIN
DECLARE #StartsWithPlus BIT = 0
--check if the mobile starts with a plus(+)
IF LEFT(#Mobile, 1) = '+'
BEGIN
SET #StartsWithPlus = 1
--Take out the plus before using the regex to eliminate invalid characters
SET #Mobile = RIGHT(#Mobile, LEN(#Mobile)-1)
END
WHILE PatIndex('%[^0-9]%', #Mobile) > 0
SET #Mobile = Stuff(#Mobile, PatIndex('%[^0-9]%', #Mobile), 1, '')
IF #StartsWithPlus = 1
SET #Mobile = '+' + #Mobile
RETURN #Mobile
END